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