如何使用struct约束将方法调用到未知结构

时间:2015-01-15 14:36:14

标签: 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);
    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约束的方法?

4 个答案:

答案 0 :(得分:2)

我很高兴被另一个答案证明是错的,但我认为如果不采取更多的反思,这是可能的。请参阅下文,了解让我怀疑这一点的原因。请参阅答案的结尾以获得基于反射的解决方案。

实用建议:我只是放弃FooStructFooClass方法的约束,另外:

  • 要么使它们成为非泛型的,要么接受object类型的参数(无论如何都是val被声明为的)。如果这些方法只是通过object,那么这些方法是通用的没有任何好处;

  • 在调用val / object之前,
  • 或将TFooStruct投射到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();
    }
}