问题很简单:我正在使用反射来获取值。然后,如果它是struct
,我正在调用方法FooStruct
,否则FooClass
:
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(value);
object obj = type.IsValueType ? val.FooStruct() : val.FooClass();
fieldInfo.SetValue(x, obj);
}
问题是FooStruct
有一个约束:
public static T FooStruct<T>(this T value) where T : struct
{
//...
}
所以问题是:是否可以为包含没有反射的盒装结构实例的对象调用struct
约束的方法?
答案 0 :(得分:2)
我很高兴被另一个答案证明是错的,但我认为如果不采取更多的反思,这是可能的。请参阅下文,了解让我怀疑这一点的原因。请参阅答案的结尾以获得基于反射的解决方案。
实用建议:我只是放弃FooStruct
和FooClass
方法的约束,另外:
要么使它们成为非泛型的,要么接受object
类型的参数(无论如何都是val
被声明为的)。如果这些方法只是通过object
,那么这些方法是通用的没有任何好处;
val
/ object
之前,或将T
从FooStruct
投射到FooClass
。
为什么似乎无法按照您的要求进行操作?您正在尝试将静态类型为object
的表达式(即val
)转换为是静态类型<T> where T : struct
或<T> where T : class
(为了在T
上调用相应的扩展方法)。也就是说,您正试图在foreach
循环中动态引入新的类型变量。不幸的是,引入类型变量的唯一方法是提前声明它,即在方法的签名中作为一些泛型类型参数T
;然后你的方法中的代码 就可以选择它所代表的实际类型 - 它是确定T
的调用代码。
基于反思的解决方案:
// determine which method ought to be called based on `val`'s run-time type.
// (for C# 6 and later, use the `nameof` operator instead of hard-coding method names)
Type type = val.GetType();
string fooName = type.IsValueType ? "FooStruct" : "FooClass";
// bind to the generic method and supply the type argument for it:
// (I'm assuming that your extension methods are defined in `FooMethodsClass`.)
MethodInfo fooOpen = typeof(FooMethodsClass).GetMethod(fooName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo foo = fooOpen.MakeGenericMethod(new Type[] { type });
// invoke the generic (extension) method with `val` as the `this` argument:
foo.Invoke(null, new object[] { val });
答案 1 :(得分:1)
动态变量支持将适当地设置T
。我经常使用这个技巧。试试这样:
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
dynamic val = fieldInfo.GetValue(value);
object obj = type.IsValueType ? Utilities.FooStruct(val) : Utilities.FooClass(val);
fieldInfo.SetValue(x, obj);
}
答案 2 :(得分:1)
显然你可以用反射来调用这些方法,它们可以毫无问题地工作:
using System;
using System.Reflection;
namespace DemoDynamicT
{
public static class Utilities
{
public static T FooStruct<T>(this T value) where T:struct
{
return default(T);
}
public static T FooClass<T>(this T value) where T : class
{
return default(T);
}
}
public class Program
{
class TestClass
{
public TestStruct StructField;
}
struct TestStruct
{
public int x;
int y;
}
public static void Main()
{
var x = new TestClass();
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(x);
var methodInfo = typeof(Utilities).GetMethod(fieldInfo.FieldType.IsValueType ? "FooStruct" : "FooClass");
var toBeCalled = methodInfo.MakeGenericMethod(fieldInfo.FieldType);
object obj = toBeCalled.Invoke(null, new [] {val});
fieldInfo.SetValue(x, obj);
}
}
}
}
答案 3 :(得分:0)
我认为你不能直接这样做。您可以尝试这样的解决方法:
public static class Utilities
{
public static ValueType FooStruct(this ValueType value)
{
//put your code here
return default(ValueType);
}
public static object FooClass(this object value)
{
//put your code here
return null;
}
public static T FooStruct<T>(this T value) where T: struct
{
return (T) FooStruct(value);
}
public static T FooClass<T>(this T value) where T: class
{
return (T) FooClass(value);
}
}
public class Program
{
class TestClass
{
public TestStruct StructField;
}
struct TestStruct
{
int x;
int y;
}
public static void Main()
{
var x = new TestClass();
Type type = x.GetType();
foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
var val = fieldInfo.GetValue(x);
object obj = fieldInfo.FieldType.IsValueType ? ((ValueType)val).FooStruct() : val.FooClass();
fieldInfo.SetValue(x, obj);
}
//Generic call
var structVar = new TestStruct();
structVar.FooStruct();
}
}