C# 如何将具有结构约束的方法调用到未知结构
问题很简单:我使用反射来获得一个值。然后,如果它是一个C# 如何将具有结构约束的方法调用到未知结构,c#,.net,reflection,struct,C#,.net,Reflection,Struct,问题很简单:我使用反射来获得一个值。然后,如果它是一个struct,我将调用一个方法FooStruct,否则FooClass: Type type = x.GetType(); foreach (var fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var val = fieldInfo.GetValue(value); ob
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
{
//...
}
publicstatictfoostruct(这个T值),其中T:struct
{
//...
}
所以问题是:对于一个包含装箱结构实例的对象,有没有可能调用带有
struct
约束的方法而不进行反射?我很乐意被另一个答案证明是错误的,但我认为如果不进行更多的反射,这是不可能的。请参阅下文,了解让我怀疑这一点的原因。有关基于反射的解决方案,请参见答案的末尾
实用建议:我只需放弃对FooStruct
和FooClass
方法的约束,另外:
- 要么使它们非泛型,要么接受
类型的参数(无论如何,这就是object
所声明的)。如果这些方法只传递val
s,那么将它们作为泛型方法没有任何好处object
- 或者在调用
/FooStruct
之前,将FooClass
从val
强制转换为object
T
对象
(即val
)表达式转换为静态类型的where T:struct
或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 });
动态变量支持将适当设置
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);
}
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();
}
}
我认为您不能直接执行此操作。您可以尝试以下解决方法:
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);
}
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();
}
}
公共静态类实用程序
{
公共静态值类型FooStruct(此值类型值)
{
//把你的代码放在这里
返回默认值(ValueType);
}
公共静态对象类(此对象值)
{
//把你的代码放在这里
返回null;
}
公共静态T FooStruct(此T值),其中T:struct
{
返回(T)FooStruct(值);
}
公共静态footclass(此T值),其中T:class
{
返回(T)类(值);
}
}
公共课程
{
类TestClass
{
公共TestStruct结构域;
}
结构测试结构
{
int x;
int-y;
}
公共静态void Main()
{
var x=新的TestClass();
Type Type=x.GetType();
foreach(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)中的var fieldInfo)
{
var val=fieldInfo.GetValue(x);
object obj=fieldInfo.FieldType.IsValueType?((ValueType)val).FooStruct():val.FooClass();
fieldInfo.SetValue(x,obj);
}
//一般呼叫
var structVar=newteststruct();
structVar.FooStruct();
}
}
显然,您可以使用反射调用这些方法,它们可以正常工作:
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);
}
}
}
}
使用系统;
运用系统反思;
命名空间解调器
{
公共静态类实用程序
{
公共静态T FooStruct(此T值),其中T:struct
{
返回默认值(T);
}
公共静态footclass(此T值),其中T:class
{
返回默认值(T);
}
}
公共课程
{
类TestClass
{
公共TestStruct结构域;
}
结构测试结构
{
公共int x;
int-y;
}
公共静态void Main()
{
var x=新的TestClass();
Type Type=x.GetType();
foreach(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)中的var fieldInfo)
{
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);
}
}
}
}
看到如何使用GetType(),您已经在使用反射:)我们没有足够的信息,因为您只在示例中使用反射。请进一步解释问题所在。您的意思是:给定一个包含装箱结构实例的对象是否可能?您想调用变量val
上的方法,对吗?您是否意识到,如果它是引用类型,您只调用了FooClass
,从未调用过更具体的方法?即使不使用反射,扩展方法也会忽略约束。有趣!两个问题:1。Utilities.FooStruct
的声明与OP的版本有什么不同,还是完全相同?2.考虑到我们正在处理动态
,在FooStruct
和FooClass
上保留泛型类型约束有什么好处吗?它应该是相同的声明。(我猜测了一下实用程序类名,因为它没有指定。)我刚刚尝试了这个,我得到类型“dynamic”必须是不可为空的值