C# 如何使用额外属性扩展类
假设我有一个名为C# 如何使用额外属性扩展类,c#,dynamic,reflection,C#,Dynamic,Reflection,假设我有一个名为Foo的类 我无法更改Foo类,但我不想使用类型为string的名为Bar的属性对其进行扩展 我还有很多类,比如Foo,所以我对“通用”解决方案感兴趣 我正在研究ExpandoObject,dynamic,它给出了我想要的结果,但我想知道它是否可以不使用dynamic static void Main(string[] args) { var foo = new Foo() { Thing = "this" }; var fooplus = Merge(foo,
Foo
的类
我无法更改Foo
类,但我不想使用类型为string
的名为Bar
的属性对其进行扩展
我还有很多类,比如Foo
,所以我对“通用”解决方案感兴趣
我正在研究ExpandoObject
,dynamic
,它给出了我想要的结果,但我想知道它是否可以不使用dynamic
static void Main(string[] args)
{
var foo = new Foo() { Thing = "this" };
var fooplus = Merge(foo, new { Bar = " and that" });
Console.Write(string.Concat(fooplus.Thing, fooplus.Bar));
Console.ReadKey();
}
public class Foo
{
public string Thing { get; set; }
}
public static dynamic Merge(object item1, object item2)
{
if (item1 == null || item2 == null)
return item1 ?? item2 ?? new ExpandoObject();
dynamic expando = new ExpandoObject();
var result = expando as IDictionary<string, object>;
foreach (System.Reflection.PropertyInfo fi in item1.GetType().GetProperties())
{
result[fi.Name] = fi.GetValue(item1, null);
}
foreach (System.Reflection.PropertyInfo fi in item2.GetType().GetProperties())
{
result[fi.Name] = fi.GetValue(item2, null);
}
return result;
}
static void Main(字符串[]args)
{
var foo=new foo(){Thing=“this”};
var fooplus=Merge(foo,new{Bar=“and that”});
Write(string.Concat(fooplus.Thing,fooplus.Bar));
Console.ReadKey();
}
公开课Foo
{
公共字符串对象{get;set;}
}
公共静态动态合并(对象项1、对象项2)
{
if(item1==null | | item2==null)
返回item1??item2??新的expandooobject();
动态expando=新的ExpandoObject();
var结果=作为IDictionary的expando;
foreach(item1.GetType().GetProperties()中的System.Reflection.PropertyInfo-fi)
{
结果[fi.Name]=fi.GetValue(item1,null);
}
foreach(item2.GetType().GetProperties()中的System.Reflection.PropertyInfo-fi)
{
结果[fi.Name]=fi.GetValue(item2,null);
}
返回结果;
}
在没有访问类定义的情况下,最好创建一个从目标类派生的类。除非原始代码是密封的,否则通过使用Reflection.Emit和运行时代码生成可以相对轻松地解决问题
假设现在您有以下要扩展的类
public class Person
{
public int Age { get; set; }
}
此类表示一个人,并包含一个名为Age的属性来表示该人的年龄
在您的情况下,您还希望添加一个string类型的Name属性来表示此人的姓名
最简单、最精简的解决方案是定义以下接口
public interface IPerson
{
string Name { get; set; }
int Age { get; set; }
}
class DynamicExtension<T>
{
public K ExtendWith<K>()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Module");
var type = module.DefineType("Class", TypeAttributes.Public, typeof(T));
type.AddInterfaceImplementation(typeof(K));
foreach (var v in typeof(K).GetProperties())
{
var field = type.DefineField("_" + v.Name.ToLower(), v.PropertyType, FieldAttributes.Private);
var property = type.DefineProperty(v.Name, PropertyAttributes.None, v.PropertyType, new Type[0]);
var getter = type.DefineMethod("get_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, v.PropertyType, new Type[0]);
var setter = type.DefineMethod("set_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, null, new Type[] { v.PropertyType });
var getGenerator = getter.GetILGenerator();
var setGenerator = setter.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
property.SetSetMethod(setter);
type.DefineMethodOverride(getter, v.GetGetMethod());
type.DefineMethodOverride(setter, v.GetSetMethod());
}
return (K)Activator.CreateInstance(type.CreateType());
}
}
此接口将用于扩展类,它应包含当前类包含的所有旧属性以及要添加的新属性。原因很快就会弄清楚
现在,您可以使用下面的类定义,通过在运行时创建一个新类型来实际扩展您的类,该类型也将使它从上述接口派生
public interface IPerson
{
string Name { get; set; }
int Age { get; set; }
}
class DynamicExtension<T>
{
public K ExtendWith<K>()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Module");
var type = module.DefineType("Class", TypeAttributes.Public, typeof(T));
type.AddInterfaceImplementation(typeof(K));
foreach (var v in typeof(K).GetProperties())
{
var field = type.DefineField("_" + v.Name.ToLower(), v.PropertyType, FieldAttributes.Private);
var property = type.DefineProperty(v.Name, PropertyAttributes.None, v.PropertyType, new Type[0]);
var getter = type.DefineMethod("get_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, v.PropertyType, new Type[0]);
var setter = type.DefineMethod("set_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, null, new Type[] { v.PropertyType });
var getGenerator = getter.GetILGenerator();
var setGenerator = setter.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
property.SetSetMethod(setter);
type.DefineMethodOverride(getter, v.GetGetMethod());
type.DefineMethodOverride(setter, v.GetSetMethod());
}
return (K)Activator.CreateInstance(type.CreateType());
}
}
DynamicExtension类定义现在更改为以下内容
public interface IPerson
{
string Name { get; set; }
Person Person { get; set; }
}
class DynamicExtension<T>
{
public T Extend()
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Assembly"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Module");
var type = module.DefineType("Class", TypeAttributes.Public);
type.AddInterfaceImplementation(typeof(T));
foreach (var v in typeof(T).GetProperties())
{
var field = type.DefineField("_" + v.Name.ToLower(), v.PropertyType, FieldAttributes.Private);
var property = type.DefineProperty(v.Name, PropertyAttributes.None, v.PropertyType, new Type[0]);
var getter = type.DefineMethod("get_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, v.PropertyType, new Type[0]);
var setter = type.DefineMethod("set_" + v.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, null, new Type[] { v.PropertyType });
var getGenerator = getter.GetILGenerator();
var setGenerator = setter.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, field);
getGenerator.Emit(OpCodes.Ret);
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, field);
setGenerator.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
property.SetSetMethod(setter);
type.DefineMethodOverride(getter, v.GetGetMethod());
type.DefineMethodOverride(setter, v.GetSetMethod());
}
var instance = (T)Activator.CreateInstance(type.CreateType());
foreach (var v in typeof(T).GetProperties().Where(x => x.PropertyType.GetConstructor(new Type[0]) != null))
{
instance.GetType()
.GetProperty(v.Name)
.SetValue(instance, Activator.CreateInstance(v.PropertyType), null);
}
return instance;
}
}
类动态扩展
{
公共电话号码(续)
{
var assembly=AppDomain.CurrentDomain.DefinedDynamicAssembly(新的AssemblyName(“assembly”),AssemblyBuilderAccess.Run);
var模块=组件。定义动态模块(“模块”);
var type=module.DefineType(“Class”,TypeAttributes.Public);
类型。AddInterfaceImplementation(类型(T));
foreach(typeof(T).GetProperties()中的变量v)
{
var field=type.DefineField(“\u3”+v.Name.ToLower(),v.PropertyType,FieldAttributes.Private);
var property=type.DefineProperty(v.Name,PropertyAttributes.None,v.PropertyType,新类型[0]);
var getter=type.DefineMethod(“get_”+v.Name,MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual,v.PropertyType,新类型[0]);
var setter=type.DefineMethod(“set_”+v.Name,MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual,null,新类型[]{v.PropertyType});
var getGenerator=getter.GetILGenerator();
var setGenerator=setter.GetILGenerator();
getGenerator.Emit(操作码.Ldarg_0);
getGenerator.Emit(操作码.Ldfld,字段);
getGenerator.Emit(操作码.Ret);
setGenerator.Emit(操作码.Ldarg_0);
setGenerator.Emit(操作码.Ldarg_1);
setGenerator.Emit(操作码.Stfld,字段);
setGenerator.Emit(操作码.Ret);
SetGetMethod(getter);
属性。SetSetMethod(setter);
type.DefineMethodOverride(getter,v.getMethod());
type.DefineMethodOverride(setter,v.GetSetMethod());
}
var instance=(T)Activator.CreateInstance(type.CreateType());
foreach(typeof(T).GetProperties()中的var v,其中(x=>x.PropertyType.GetConstructor(新类型[0])!=null))
{
instance.GetType()
.GetProperty(v.Name)
.SetValue(实例,Activator.CreateInstance(v.PropertyType),null);
}
返回实例;
}
}
现在,我们可以简单地执行以下代码行以获得所有适当的值
class Program
{
static void Main(string[] args)
{
var extended = new DynamicExtension<IPerson>().Extend();
extended.Person.Age = 25;
extended.Name = "Billy";
Console.WriteLine(extended.Name + " is " + extended.Person.Age);
Console.Read();
}
}
类程序
{
静态void Main(字符串[]参数)
{
var extended=new DynamicExtension().Extend();
延长的个人年龄=25岁;
extended.Name=“比利”;
Console.WriteLine(extended.Name+“是”+extended.Person.Age);
Console.Read();
}
}
由于我的评论越来越冗长,我想我应该添加一个新的答案。这个答案完全是马里奥的作品和思想,只有我的小补充来说明我想表达的内容
对mario的示例进行了一些小的更改,这将使其工作得非常好,即,只需更改现有属性作为类对象添加的事实,而不是复制整个类。不管怎样,这看起来是这样的(只添加了修订的部分,其他所有部分仍然按照马里奥的回答):
对于IPerson
接口,我们添加了实际的Person
类,而不是复制属性:
public interface IPerson
{
// extended property(s)
string Name { get; set; }
// base class to extend - tho we should try to overcome using this
Person Person { get; set; }
}
这转化为以下各项的更新使用:
static void Main(string[] args)
{
var extended = new DynamicExtension<Person>().ExtendWith<IPerson>();
var pocoPerson = new Person
{
Age = 25,
FaveQuotation = "2B or not 2B, that is the pencil"
};
// the end game would be to be able to say:
// extended.Age = 25; extended.FaveQuotation = "etc";
// rather than using the Person object along the lines below
extended.Person = pocoPerson;
extended.Name = "Billy";
Console.WriteLine(extended.Name + " is " + extended.Person.Age
+ " loves to say: '" + extended.Person.FaveQuotation + "'");
Console.ReadKey();
}
static void Main(字符串[]args)
{
var extended=new DynamicExtension().extendedwith();
var pocoPerson=新人
{
年龄=25岁,
favequote=“2B或不是2B,那是铅笔”
};
//末日
var className = "ClassA";
var baseType = typeof(List<string>);
var typeExtender = new TypeExtender(className, baseType);
typeExtender.AddProperty("IsAdded", typeof(bool));
typeExtender.AddProperty<double>("Length");
var returnedClass = typeExtender.FetchType();
var obj = Activator.CreateInstance(returnedClass);