以程序方式创建类似的类,包括方法

本文关键字:包括 方法 方式 创建 程序 | 更新日期: 2023-09-27 18:13:54

希望标题解释了我的问题,但我试图以编程方式创建类,但每个类都有不同的属性和特定的方法。

目前,每个类都是手动创建的,但如果可能的话,我想将其更改为以编程方式创建。目前,每个类看起来都像以下内容:

public class SomeEventName : EventBase
{
    public string HardwareId { get; set; }
    public ushort ComponantStatus { get; set; }
    public override string ToString()
    {
        return string.Format("SomeEventName event - HardwareId: {0}, GeneratedTime: {1}, ReceivedTime: {2}", HardwareId , GeneratedTime, ReceivedTime);
    }
    public static SomeEventName Default(string tvmId, DateTime createTime, DateTime receiveTime)
    {
        return new SomeEventName 
        {
            HardwareId = hardwareId,
            GeneratedTime = generatedTime,
            ReceivedTime = receivedTime,
            AdaptedTime = DateTime.UtcNow
        };
    }
}

我有替换的名字,但本质上是

  • SomeEventName将是事件的名称
  • 属性将特定于该事件
  • ToString覆盖需要替换SomeEventName用于类的实际类型
  • Default方法将需要返回该类的一个实例

我知道类可以通过使用Reflection.Emit的代码来创建,但当涉及到方法时,我只看到了通过IL代码来创建的方法,我想避免这种方法。我可以更改ToString覆盖以使用参数打印类类型,但我不确定如何处理Default方法。

必须具备的是,我显然需要能够实例化所述类,并且我需要以下代码行来返回实际类型,而不是一些通用名称:

typeof(SomeEventName);

因此,Reflection是我的最佳选择吗?如果是,有没有一种方法可以在不必使用IL代码的情况下处理Default方法?或者我可以用另一种方式来处理这个问题吗?

以程序方式创建类似的类,包括方法

假设您想用给定的类编写一个程序集,则可以使用runsharp。一旦掌握了语法,就可以在运行时编写程序集,以便从其他应用程序中引用。

声明非常直接,您可以使用它来编写有效的.NET程序集(带有可反射的.NET代码(,而不必处理IL.

如果您使用C#6,您可以使用Roslyn服务动态编译C#。请参见此处的示例。

所以我有一种方法可以通过使用泛型来实现我所需要的东西,并从答案到-在C#中动态创建一个类

第一件事是将SomeEventName转换为使用泛型,这样我就可以将其用作基类。GetType()使我能够区分可以创建的每种类型。这给了我:

public class EventBase
{
    public string HardwareId { get;set; }
    public DateTime MessageGeneratedTime { get; set; }
    public DateTime MessageReceivedTime { get; set; }
    public DateTime MessageAdaptedTime { get; set; }
    public override string ToString()
    {
        return string.Format("{0} event - HardwareId: {1}, CreateTime: {2}, MessageReceivedTime: {3}", GetType(), HardwareId, MessageGeneratedTime, MessageReceivedTime);
    }
    public T Default<T>(string dardwareId, DateTime createTime, DateTime receiveTime) where T : TestBase, new()
    {
        var instance = new T
        {
            HardwareId = hardwareId,
            MessageGeneratedTime = createTime,
            MessageReceivedTime = receiveTime,
            MessageAdaptedTime = DateTime.UtcNow
        };
        return instance;
    }
}

接下来,我需要实例化每个类型。我一开始只做了一个,但我可以很容易地扩展我必须创建的其他类型。因此,在上面链接的答案的帮助下,我最终得到了:

var myType = CompileResultType(properties);
var myObject = Activator.CreateInstance(myType);

虽然我不会包括这个答案中的所有代码,但关键是:

private static TypeBuilder GetTypeBuilder()
{
    var typeSignature = "SomeEventName";
    var an = new AssemblyName(typeSignature);
    AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("TypeOfTest");
    TypeBuilder tb = moduleBuilder.DefineType(typeSignature
        , TypeAttributes.Public |
        TypeAttributes.Class |
        TypeAttributes.AutoClass |
        TypeAttributes.AnsiClass |
        TypeAttributes.BeforeFieldInit |
        TypeAttributes.AutoLayout, 
        typeof(TestBase),
        null);
    return tb;
}

在该代码中,我创建了一个SomeEventName的签名,并将其作为基类EventBase

一旦我有了typeobject,我就可以向它传递一些参数,并调用基类中的Default方法,同时使其成为泛型。

var arguements = new object[] { "QWERTY", DateTime.UtcNow, DateTime.UtcNow };
var @event = myObject.GetType().GetMethod("Default").MakeGenericMethod(myObject.GetType()).Invoke(myObject, arguements);

由于这是在控制台应用程序中,因此我可以根据上面代码中的参数调用ToString覆盖,并给出以下输出:

SomeEventName事件-设备:QWERTY,创建时间:2015年10月5日15:18:25,消息接收时间:2015年10月5日15:18:25

另一个要求是我可以创建属性,在上面的代码中,你可以看到我调用CompileResultType传递了一个参数列表。所以我所做的就是:

var properties = new Dictionary<string, string>();
properties.Add("Field1", "System.String");
properties.Add("Field2", "System.Int32");

然后我当然可以用设置它们的值

myObject.GetType().GetProperty("Field1").SetValue(myObject, "Hello world");
myObject.GetType().GetProperty("Field2").SetValue(myObject, 987);

然后用将它们输出到控制台窗口

Console.WriteLine("Field1: {0}", myObject.GetType().GetProperty("Field1").GetValue(myObject));
Console.WriteLine("Field2: {0}", myObject.GetType().GetProperty("Field2").GetValue(myObject));

虽然它可能不是最好的解决方案,而且它确实有缺点,比如一切都变成了字符串,但它做了所需要的。如前所述,这只适用于一种类型,但可以通过将signature作为字符串传递给CompileResultType来轻松扩展,而CCD_22又将其传递给CCD23。