Expression.Call on Expression.PropertyOrField on Interface IList<T> 抛出 InvalidOperationExcepti



因此,如果我尝试对IList类型的类的属性调用"Clear",它就会抛出。如果我对IList类型的常量做同样的事情,程序运行得很好。

class Program
{
    static void Main(string[] args)
    {
        var parent = new Parent(new List<SomeClass>());
        parent.Data.Add(new SomeClass("Test"));
        Console.WriteLine(parent.Data.Count);
        var expr = Expression.Lambda(Expression.Call(Expression.Constant(parent.Data), "Clear", null, null), null);
        var x = expr.Compile();
        x.DynamicInvoke();
        Console.WriteLine(parent.Data.Count);
        var expr2 = Expression.Lambda(Expression.Call(Expression.PropertyOrField(Expression.Constant(parent), "Data"), "Clear", null, null), null);
        var x2 = expr.Compile();
        x2.DynamicInvoke();
        Console.WriteLine(parent.Data.Count);
        Console.ReadLine();
    }
    public class Parent
    {
        public Parent(IList<SomeClass> data)
        {
            this.Data = data;
        }
        public IList<SomeClass> Data { get; set; }
    }
    public class SomeClass
    {
        public SomeClass(string value) { }
    }
}
// output:
// 1
// 0
// Exception of type: InvalidOperationException was thrown

这只是一个错误吗?还是有某种逻辑原因导致

它的行为是这样的,因为接口中的"继承"在.net中是如何工作的

public interface ITest 
{
    string Property{get;set;}
}

public interface ISubTest : ITest
{
}

然后称之为:

typeof(ITest).GetProperty("Property"); // returns property
typeof(ISubTest).GetProperty("Property"); // returns null 

所以在您的情况下,强制转换为ICollection会起作用,因为Clear方法是在ICollection接口中定义的,而不是在IList接口上。

关于接口类型派生的更多信息,请参阅"为什么继承接口的成员不能使用反射?"?

我会将任何响应现有连接错误或对此行为有合理解释的人标记为答案,但。。。解决方案是在调用之前强制转换为ICollection

        var expr2 = Expression.Lambda(Expression.Call(
            Expression.Convert(Expression.PropertyOrField(Expression.Constant(parent), "Data"), typeof(ICollection<>).MakeGenericType(parent.Data.GetType().GetGenericArguments()))
            , "Clear", null, null), null);

相关内容

最新更新