В прошлом я сталкивался с той же проблемой и нашел это решение на основе статьи Отлично от Кайла sh Чандра Бехера .
Секрет основан на использовании System.Reflection.Emit
, который предоставляет классы, которые позволяют компилятору или инструменту генерировать метаданные и промежуточный язык Microsoft (MSIL) и дополнительно генерировать PE-файл на диске. Основными клиентами этих классов являются скриптовые движки и компиляторы.
Для любопытных и увлеченных вы можете go впереди: System.Reflection.Emit Namespace и Введение в создание Dynami c Типы с Reflection.Emit
Решение :
List<dynamic>
, Dictionary<>
, ExpandoObject
не может работать, потому что отражение будет остановился на иерархии первого уровня вашего экземпляра класса MyStatiClass
. Единственное решение, которое я нашел, - это динамическое создание полного MyStatiClass
во время выполнения, включая пространство имен экземпляра, имя класса, имена свойств, атрибуты и т. Д. c. .
Это код ViewModel, соответствующий вашему вопросу:
public class MainViewModel : ViewModelBase
{
private ObservableCollectionEx<dynamic> _myCollectionVM = new ObservableCollectionEx<dynamic>();
public ObservableCollectionEx<dynamic> MyCollectionVM
{
get => _myCollectionVM;
set => Set(nameof(MyCollectionVM), ref _myCollectionVM, value);
}
public MainViewModel()
{
MyClassBuilder myClassBuilder = new MyClassBuilder("DynamicClass");
var myDynamicClass = myClassBuilder.CreateObject(new string[3] { "ID", "Name", "Address" }, new Type[3] { typeof(int), typeof(string), typeof(string) });
MyCollectionVM.Add(myDynamicClass);
// You can either change properties value like the following
myDynamicClass.ID = 1;
myDynamicClass.Name = "John";
myDynamicClass.Address = "Hollywood boulevard";
}
}
Замечание : проверки компиляции и Intellisense не будут работать c типов, так что позаботьтесь о синтаксисе свойств, иначе вы получите исключение во время выполнения.
Затем Dynami c Class Factory Builder, который создаст полный класс во время выполнения:
/// <summary>
/// Dynamic Class Factory Builder
/// </summary>
public class MyClassBuilder
{
AssemblyName asemblyName;
public MyClassBuilder(string ClassName)
{
asemblyName = new AssemblyName(ClassName);
}
public dynamic CreateObject(string[] PropertyNames, Type[] Types)
{
if (PropertyNames.Length != Types.Length)
{
throw new Exception("The number of property names should match their corresopnding types number");
}
TypeBuilder DynamicClass = CreateClass();
CreateConstructor(DynamicClass);
for (int ind = 0; ind < PropertyNames.Count(); ind++)
CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
Type type = DynamicClass.CreateType();
return Activator.CreateInstance(type);
}
private TypeBuilder CreateClass()
{
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType(asemblyName.FullName
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return typeBuilder;
}
private void CreateConstructor(TypeBuilder typeBuilder)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
}
private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = typeBuilder.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 = typeBuilder.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);
}
}
Наслаждайтесь.