【问题标题】:Expression.Convert(..., someGenericType) throws ArgumentException when used with generic typeExpression.Convert(..., someGenericType) 与泛型类型一起使用时抛出 ArgumentException
【发布时间】:2015-11-22 22:31:50
【问题描述】:

我有这个方法,使用Expressions 创建字段getter:

public static Func<object, T> CreateFieldValueGetter<T>(this Type declaringType, FieldInfo fieldToGet) {
    var paramExp = Expression.Parameter(typeof(object));
    // ArgumentException if declaringType describes generic-type:
    var cast = Expression.Convert(paramExp, declaringType);
    var body = Expression.Field(cast, fieldToGet);
    return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
}

在我给它一个通用类型之前它工作得很好:

class DataErrorNotifyingViewModelBase<TErr> : ViewModelBase, INotifyDataErrorInfo 
    where TErr : struct, IConvertible, IComparable, IFormattable
{
    // ...
}

这边:

var vm = new DataErrorNotifyingViewModelBase<MyErrorsTypeEnum> ();
var type = vm.GetType();
// ArgumentException:
var getter = type.CreateFieldValueGetter<PropertyChangedEventHandler>(type.GetField("PropertyChanged"));

这是我得到的例外:

Exception thrown: 'System.ArgumentException' in System.Core.dll

Additional information: Type GuiHelpers.DataErrorNotifyingViewModelBase`1[TErr] is a generic type definition

虽然简单的铸造工作:

var vm = new DataErrorNotifyingViewModelBase<PrintDialogError>();
var obj = (object) vm;

那么我怎样才能给它提供泛型类型呢?我是否仅限于非泛型类型?

编辑 - 解决方案:

Kaveh Hadjari 抓住了它:

传递t = typeof (Dictionary&lt;T, int&gt;) 将引发ArgumentException,因为t.GetGenericArguments()[0].IsGenericParametertrue(尽管t.GetGenericArguments()[1].IsGenericParameterfalse!)

传递t = typeof (Dictionary&lt;int, int&gt;) 类型可以正常工作,因为t.GetGenericArguments() 数组的任何元素都没有IsGenericParameter == true

【问题讨论】:

  • 您的代码无法编译。 type.CreateFieldValueGetter&lt;PropertyChangedEventHandler&gt;(type.GetField("PropertyChanged")); 无法编译,因为您的方法不是扩展方法。 然而,解决这个问题后,您的代码可以正常工作。请准确地向我们展示您正在使用的代码。
  • 无法重现该问题;似乎对我来说工作正常。也许你把这个例子简化得太多了?
  • 请看这个简化的小提琴,表明你的 CreateFieldValueGetter 方法确实有效:dotnetfiddle.net/iVTzio
  • @Rob,已修复,谢谢。

标签: c# generics lambda expression expression-trees


【解决方案1】:

泛型类型是许多不同专用类型的模板,在运行时泛型类型和“实例化”类型之间存在差异。对 Expression.Convert 的调用可能失败的一个可能原因可能是您为其提供了通用版本的类型,而不是设置了类型变量的专用版本。

更新:我想有一个很好的理由,这种方法永远不会适用于泛型类型。考虑将类型变量用作泛型类中字段的类型的情况。由于类型大小(reference、Boolean、short、int、long 等)可以是可变的,这意味着它可以以可变的方式偏移泛型类的不同专业化中的其他字段的内存地址。如果未设置所有变量,您如何提前知道哪个字段长度以及地址偏移量?您不能,因此我们无法确定我们可能要为其创建 getter 的字段的地址。唯一的解决方案是实际创建一个 getter,它依赖于对您调用该 getter 的每个对象使用反射,这将产生比想象的更高的成本,如果您对该解决方案感到满意,您可能还需要一个方法使用反射获取字段的值,而无需实际创建这些 getter。

【讨论】:

    【解决方案2】:

    “我是否仅限于非泛型类型?”

    不,当然不是。错误消息很清楚,但与您提供的代码示例不一致。您似乎为类型传递了原始泛型类型定义(即类型参数具有未指定值),而不是构造的泛型类型(即类型参数具有指定值)。

    不幸的是,如果没有可靠地重现问题的a good, minimal, complete code example,就不可能准确地知道你做错了什么。我只能说你做错了什么。如果您需要更具体的建议,请编辑您的帖子,使其包含一个好的代码示例。

    对于它的价值,这里有一个完整的代码示例,它演示了您的方法在泛型类型上工作正常:

    class A<T>
    {
        public int field1;
        public T field2;
        public event EventHandler Event1;
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            A<bool> a = new A<bool>();
            Func<object, int> field1Getter =
                CreateFieldValueGetter<int>(a.GetType(), a.GetType().GetField("field1"));
            Func<object, bool> field2Getter =
                CreateFieldValueGetter<bool>(a.GetType(), a.GetType().GetField("field2"));
            Func<object, EventHandler> event1Getter =
                CreateFieldValueGetter<EventHandler>(a.GetType(), a.GetType()
                    .GetField("Event1", BindingFlags.NonPublic | BindingFlags.Instance));
        }
    
        static Func<object, T> CreateFieldValueGetter<T>(Type declaringType, FieldInfo fieldToGet)
        {
            var paramExp = Expression.Parameter(typeof(object));
            // ArgumentException if declaringType describes generic-type:
            var cast = Expression.Convert(paramExp, declaringType);
            var body = Expression.Field(cast, fieldToGet);
    
            return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
        }
    }
    

    这里唯一的问题是,要获取事件的字段,您必须指定适合该字段的 BindingFlags(特别是,它是非公开的,因此默认搜索 GetField() 不会找到它)。您显示的代码不正确,但它没有解释您遇到的异常。

    【讨论】:

      猜你喜欢
      • 2015-09-12
      • 2019-06-03
      • 2012-11-28
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 2011-07-24
      相关资源
      最近更新 更多