C# 反射和泛型类型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/196936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reflection and generic types
提问by Aaron Powell
I'm writing some code for a class constructor which loops through all the properties of the class and calls a generic static method which populates my class with data from an external API. So I've got this as an example class:
我正在为类构造函数编写一些代码,该类构造函数遍历类的所有属性并调用通用静态方法,该方法用来自外部 API 的数据填充我的类。所以我把它作为示例类:
public class MyClass{
public string Property1 { get; set; }
public int Property2 { get; set; }
public bool Property3 { get; set; }
public static T DoStuff<T>(string name){
// get the data for the property from the external API
// or if there's a problem return 'default(T)'
}
}
Now in my constructor I want something like this:
现在在我的构造函数中,我想要这样的东西:
public MyClass(){
var properties = this.GetType().GetProperties();
foreach(PropertyInfo p in properties){
p.SetValue(this, DoStuff(p.Name), new object[0]);
}
}
So the above constructor will thrown an error because I'm not supplying the generic type.
所以上面的构造函数会抛出一个错误,因为我没有提供泛型类型。
So how do I pass in the type of the property in?
那么如何传入属性的类型呢?
采纳答案by Marc Gravell
Do you want to call DoStuff<T> with T = the type of each property? In which case, "as is" you would need to use reflection and MakeGenericMethod - i.e.
你想用 T = 每个属性的类型调用 DoStuff<T> 吗?在这种情况下,“按原样”您需要使用反射和 MakeGenericMethod - 即
var properties = this.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
object value = typeof(MyClass)
.GetMethod("DoStuff")
.MakeGenericMethod(p.PropertyType)
.Invoke(null, new object[] { p.Name });
p.SetValue(this, value, null);
}
However, this isn't very pretty. In reality I wonder if it wouldn't be better just to have:
然而,这不是很漂亮。实际上,我想知道是否只是拥有:
static object DoStuff(string name, Type propertyType);
... and then
object value = DoStuff(p.Name, p.PropertyType);
What does the generics give you in this example? Note that value-types will still get boxed etc during the reflection call - and even then boxing isn't as bad as you might think.
在这个例子中,泛型给了你什么?请注意,在反射调用期间,值类型仍然会被装箱等 - 即便如此,装箱也没有您想象的那么糟糕。
Finally, in many scenarios, TypeDescriptor.GetProperties() is more appropriate than Type.GetProperties() - allows for flexible object models etc.
最后,在许多情况下,TypeDescriptor.GetProperties() 比 Type.GetProperties() 更合适 - 允许灵活的对象模型等。
回答by Jon Skeet
Was your constructor code meant to read like this:
你的构造函数代码是不是应该这样读:
public MyClass(){
var properties = this.GetType().GetProperties();
foreach(PropertyInfo p in properties){
p.SetValue(this, DoStuff(p.Name), new object[0]);
}
}
? Note the DoStuff
instead of MyClass
.
? 请注意DoStuff
代替MyClass
。
If so, the problem is that you're trying to use generics when they're really not applicable. The point of generics (well, one of the points) is to use compile-time type safety. Here you don't know the type at compile time! You could call the method by reflection (fetching the open form and then calling MakeGenericMethod) but that's pretty ugly.
如果是这样,问题在于您在泛型确实不适用时尝试使用它们。泛型的要点(嗯,要点之一)是使用编译时类型安全。在这里你不知道编译时的类型!您可以通过反射调用该方法(获取打开的表单,然后调用MakeGenericMethod),但这非常难看。
Does DoStuff
really need to be generic in the first place? Is it being used from elsewhere? The parameter to PropertyInfo.SetValue
is just object, so you'd still get boxing etc even if you couldcall the method generically.
首先DoStuff
真的需要通用吗?它是从其他地方使用的吗?to 的参数PropertyInfo.SetValue
只是对象,因此即使您可以通用地调用该方法,您仍然会得到装箱等。
回答by Gaspar Nagy
If you don't use DoStuff from another place, I also suggest to write a non-generic method.
如果你不使用其他地方的DoStuff,我也建议你写一个非泛型的方法。
Maybe you created the generic method to be able to use default(T). To replace that in a non-generic method, you can use Activator.CreateInstance(T) for value types and null for reference types:
也许您创建了能够使用 default(T) 的通用方法。要在非泛型方法中替换它,您可以对值类型使用 Activator.CreateInstance(T),对引用类型使用 null:
object defaultResult = type.IsValueType ? Activator.CreateInstance(type) : null