C#运行时创建类

Kata的练习题 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

public static class Kata
{
    public static bool DefineClass(string className, Dictionary<string, Type> properties, ref Type actualType)
    {
        actualType = MyTypeBuilder.CompileResultType(className, properties);
        return actualType != null;
    }
}

public static class MyTypeBuilder
{
    private const string _AssemblyName = "RuntimeAssembly";
    private const string _ModuleName = "MainModule";

    private static Lazy<ModuleBuilder> moduleBuilder = new Lazy<ModuleBuilder>(() =>
    {
        var an = new AssemblyName(_AssemblyName);
        AssemblyBuilder assemblyBuilder =  AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        return assemblyBuilder.DefineDynamicModule(_ModuleName);
    });


    public static Type CompileResultType(string className, Dictionary<string, Type> props)
    {
        Assembly dynamicAssembly = AppDomain.CurrentDomain.GetAssemblies()
            .FirstOrDefault(a => a.FullName.StartsWith(_AssemblyName + ","));
        Type existingType = (dynamicAssembly == null) ? null : dynamicAssembly.GetType(className);
        if (existingType != null) return null;

        TypeBuilder tb = GetTypeBuilder(className);
        ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

        foreach (var field in props)
            CreateProperty(tb, field.Key, field.Value);

        Type objectType = tb.CreateType();
        return objectType;
    }

    private static TypeBuilder GetTypeBuilder(string className)
    {
        TypeBuilder tb = moduleBuilder.Value.DefineType(className,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                null);
        return tb;
    }

    private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
    {
        FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

        PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
        MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();

        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, fieldBuilder);
        getIl.Emit(OpCodes.Ret);

        MethodBuilder setPropMthdBldr =
            tb.DefineMethod("set_" + propertyName,
                MethodAttributes.Public |
                MethodAttributes.SpecialName |
                MethodAttributes.HideBySig,
                null, new[] { propertyType });

        ILGenerator setIl = setPropMthdBldr.GetILGenerator();
        Label modifyProperty = setIl.DefineLabel();
        Label exitSet = setIl.DefineLabel();

        setIl.MarkLabel(modifyProperty);
        setIl.Emit(OpCodes.Ldarg_0);
        setIl.Emit(OpCodes.Ldarg_1);
        setIl.Emit(OpCodes.Stfld, fieldBuilder);

        setIl.Emit(OpCodes.Nop);
        setIl.MarkLabel(exitSet);
        setIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
        propertyBuilder.SetSetMethod(setPropMthdBldr);
    }
}

测试类

public class SomeClass
{ /* This class should not conflict with your runtime classes */ }

namespace Solution
{
using NUnit.Framework;
using System;
using System.Linq;
using System.Collections.Generic;

    [TestFixture]
    public partial class SolutionTest
    {
        [Test]
        public void BasicTest()
        {
            Random rand = new Random();
            Type myType = typeof(object);
            Dictionary<string, Type> properties;

            // Define first class
            properties = new Dictionary<string, Type> { { "SomeInt", typeof(int) }, { "SomeString", typeof(string) }, { "SomeObject", typeof(object) } };
            Kata.DefineClass("SomeClass", properties, ref myType);
            // Instantiate first class
            var myInstance = CreateInstance(myType);
            myInstance.SomeObject = myInstance;
            myInstance.SomeString = "Hey there";
            myInstance.SomeInt = 3;
            Console.WriteLine($"{myInstance.SomeObject}: {myInstance.SomeString}, {myInstance.SomeInt}");

            // Define second class
            properties = new Dictionary<string, Type> { { "AnotherObject", typeof(object) }, { "SomeDouble", typeof(double) }, { "AnotherString", typeof(string) } };
            Kata.DefineClass("AnotherClass_N" + rand.Next(100), properties, ref myType);
            // Instantiate second class
            myInstance = CreateInstance(myType);
            myInstance.AnotherObject = "User: ";
            myInstance.AnotherString = "My lucky number is ";
            myInstance.SomeDouble = 92835768;
            Console.WriteLine($"{myInstance.AnotherObject}: {myInstance.AnotherString} {myInstance.SomeDouble} ");

            // Try to redefine first class
            if (Kata.DefineClass("SomeClass", properties, ref myType) == true)
                Assert.Fail("This class is already defined");
        }
        
        [Test]
        public void RandomTests()
        {
            Random rand = new Random();
            Type myType = typeof(object);
            List<string> usedClassNames = new List<string>();

            var properties = new Dictionary<string, Type> {
                { "Name", typeof(string) }, { "Surname", typeof(string) }, { "Address", typeof(string) }, { "Location", typeof(string) },
                { "Value", typeof(int) }, { "UselessNumber", typeof(int) }, { "Speed", typeof(double) }, { "Weight", typeof(double) },
                { "Source", typeof(object) }, { "Parent", typeof(object) }, { "Trash", typeof(object) }, { "Oggetto", typeof(object) },
                { "A", typeof(byte) }, { "R", typeof(byte) }, { "G", typeof(byte) }, { "B", typeof(byte) },
            };

            var randomClasses = new string[]
            { "Jonahtan", "AnotherClass", "ThisIsNotAClass", "SeriousClass", "ThisIsClass", "ClassAndElegance", "ImTakingClasses", "Classified", "Classic", "Classical", "UpperClassMan", "ClassicClass", "IsThisAClass", "ICannotBelieveThisIsNotAClass",
              "ICannotBelieveThisIsAClass", "SimpleClass", "WhatIsAStructDoingHereGetOutMan"};

            var randomStrings = new string[] 
            { "Hello", "This is a string", "Good Job", "You can do better than this", "You are playing it wrong give me the controller", "What do you call a tick on the moon? A luna-tick.", "A ham sandwich walks into a bar and the bartender says, 'Sorry, we don't serve food in here.'", "Oxygen and magnesium went out on a date, and everyone was like, O Mg" , "In order to understand recursion you must first understand recursion.",
                "Why do Java programmers wear glasses? Because they don’t C#!", "I knew you could do it!" };

            if (myType.FullName.Contains("ExpandoObject"))
                Assert.Fail("ExpandoObject not allowed");

            for (int i = 0; i < 40; i++)
            {
                List<object> expected = new List<object>();
                var currentProperties = properties.Where(x => rand.NextDouble() > 0.3).ToDictionary(x => x.Key, x => x.Value);
                string className = randomClasses[rand.Next(randomClasses.Length)];

                if (Kata.DefineClass(className, currentProperties, ref myType) == true)
                {
                    Assert.False(usedClassNames.Contains(className), "The class " + className + " was already defined");
                    var myInstance = (object)CreateInstance(myType);
                    usedClassNames.Add(className);

                    foreach (KeyValuePair<string, Type> prop in currentProperties)
                    {
                        dynamic value = null;
                        if (prop.Value == typeof(string))
                        {
                            value = randomStrings[rand.Next(randomStrings.Length)];
                        }
                        else if (prop.Value == typeof(int))
                        {
                            value = rand.Next(100000);
                        }
                        else if (prop.Value == typeof(double))
                        {
                            value = rand.NextDouble() * 3;
                        }
                        else if (prop.Value == typeof(byte))
                        {
                            value = (byte)rand.Next(256);
                        }
                        else
                        {
                            value = (value ?? "Empty");
                        }
                        expected.Add(value);
                        var p = myInstance.GetType().GetProperty(prop.Key);
                        Assert.IsNotNull(p, "The property named '" + prop.Key + "' of type '" + prop.Value + "' was not found.");
                        p.SetValue(myInstance, value);
                    }
                    var actual = myInstance.GetType().GetProperties().Select(x => x.GetValue(myInstance)).ToList();
                    CollectionAssert.AreEqual(expected, actual, "The properties values of this class are differente from the expected ones.");
                    Console.WriteLine(className + " : " + (actual.FirstOrDefault(x => x.GetType() == typeof(string)) ?? "nothing" ) + "\n" );
                }
                else
                {
                    Assert.True(usedClassNames.Contains(className), "DefineClass() returned false, but the class " + className + " was not yet defined ");
                }
            }
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值