0

Hopefully the title, explained my question, but what I am trying to do is create classes programmatically but each one will have different properties along with methods specific to each.

Currently, each class is manually created but I would like to change that to programmatically if possible. At the moment, each class will look something like the following:

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
        };
    }
}

I have substituted names but essentially

  • SomeEventName will be the name of an event
  • The properties will be specific to that event
  • The ToString override will need to substitute SomeEventName for the actual type of the class
  • The Default method will need to return an instance of the class.

I know classes can be created through code using Reflection.Emit, but when it comes to methods, I've only seen ways of doing it through IL code which I want to avoid. I could change the ToString override to use a parameter to print the class type, but I am unsure about how to handle the Default method.

The must haves are that I obviously need to be able to instantiate said classes and I need the following line of code to return the actual type and not some generic name:

typeof(SomeEventName);

Therefore, is Reflection my best bet for this and if so, is there a way to handle the Default method without having to use IL code? Or is there a different way I could approach this?

Stuart
  • 754
  • 11
  • 25
  • I've never seen such requirements (genuinely interested). What are you wanting to do with these classes? If you'd eventually instantiate them - what would compile and reference them? –  Sep 21 '15 at 09:28
  • Have you seen [T4](https://msdn.microsoft.com/en-us/library/bb126445%28v=vs.120%29.aspx)? – Rowland Shaw Sep 21 '15 at 09:38
  • @JayMee Each class would represent an event raised by hardware being monitored. So say you have a snack vending machine and a event is raised saying an item is stuck. The machine will send that event and an event (using the code above) will be created in the monitoring software which ultimately raise alerts telling someone something needs to be fixed. – Stuart Sep 21 '15 at 09:40
  • @RowlandShaw I have in the context of Entity Framework but I didn't know they could be used in this way as well. Thanks. – Stuart Sep 21 '15 at 09:44
  • @StuartL You may end up with an awful lot of classes here - I assume you have seen the DEX file format that vending machines produce? – DavidG Sep 21 '15 at 10:25
  • @DavidG We do already have a lot of classes. Vending machines was an example but we are using SNMP. So rather than having to make a code change when we need to monitor something, we are trying to go down a configuration route where possible. – Stuart Sep 21 '15 at 10:32
  • I just think it would be easier to have a single class that you can use instead of potentially thousands, especially if you ever need to store these in a database in their raw form. – DavidG Sep 21 '15 at 10:38
  • That is what I am thinking as well. As long as the end result stays the same, what happens between receiving each message and the end result, can change. – Stuart Sep 21 '15 at 10:43
  • As long as you need some specific methods (that have to be written by someone), can't you inherit your (abstract) base class `EventBase` where needed (thus being able to add any properties) and feed it with some `Func` (or whatever) to be called ? – xum59 Sep 21 '15 at 10:45

3 Answers3

0

Assuming that you want to write an assembly with that given classes, you can use runsharp. Once you got into the syntax, you can write assemblies in runtime you can reference from other applications.

The declaration is pretty straight-forward and you can use it to write valid .NET-assemblies (with reflectable .NET code) without having to deal with IL.

Waescher
  • 5,361
  • 3
  • 34
  • 51
0

If you are using C# 6 you could use Roslyn services to compile C# on the fly. See for example here.

elchido
  • 153
  • 7
0

So I have a way of achieving what I require through the use of generics and help from the answer to - Dynamically create a class in C#

The first thing was to convert SomeEventName to use generics and so that I could use it as a base class. The GetType() is so that I can distinguish between each type that could be created. This gave me:

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;
    }
}

Next I needed to instantiate each type. I started off with just one but I could easily extend what I have to create other types. So with the help of the answer linked to above, I ended up with:

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

While I won't include all of the code from that answer, the key bit was:

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;
}

Within that code, I create a signature of SomeEventName and give it my base class, EventBase.

Once I had a type and object, I could then pass it some parameters and invoke the Default method in the base class while making it a generic.

var arguements = new object[] { "QWERTY", DateTime.UtcNow, DateTime.UtcNow };

var @event = myObject.GetType().GetMethod("Default").MakeGenericMethod(myObject.GetType()).Invoke(myObject, arguements);

As this was in a console application, I could then call my ToString override, that based on the parameters in the code above, giving an output of:

SomeEventName event - Device: QWERTY, CreateTime: 05/10/2015 15:18:25, MessageReceivedTime: 05/10/2015 15:18:25

The other requirement was that I could create properties and in the code above, you can see that I call CompileResultType passing a list of parameters. So all I did was:

var properties = new Dictionary<string, string>();

properties.Add("Field1", "System.String");
properties.Add("Field2", "System.Int32");

I could then of course set their values with:

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

Then output them to the console window with:

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

While it may not be the best solution and that it does have its disadvantages such was everything becoming strings, it does what is required. As mentioned, this is only for one type, but it could easily be extended by passing the signature as a string to CompileResultType which would in turn pass it to GetTypeBuilder.

Community
  • 1
  • 1
Stuart
  • 754
  • 11
  • 25