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
    所声明的)。如果这些方法只传递
    object
    s,那么将它们作为泛型方法没有任何好处

  • 或者在调用
    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”必须是不可为空的值