排序接口时“DynamicMethod的类型所有者无效”错误

时间:2012-05-22 09:05:17

标签: .net sorting interface

我们通过Andrew Davey's使用sourceforge BindingListView<T>类将集合绑定到DataGridView并允许排序和过滤。

这适用于普通集合。但是在一种情况下,我们绑定的集合是一个接口类型,如果我们尝试对它进行排序,我们会收到此错误:

Invalid type owner for DynamicMethod

安德鲁戴维斯的代码中存在错误,因此我们很难知道从哪里开始。

        private static Comparison<T> BuildValueTypeComparison(PropertyInfo pi, ListSortDirection direction)
        {
            MethodInfo getMethod = pi.GetGetMethod();
            Debug.Assert(getMethod != null);


            DynamicMethod dm = new DynamicMethod("Get" + pi.Name, typeof(int), new Type[] { typeof(T), typeof(T) }, typeof(T), true);
            //^^^ ======== Here's the line reporting the error=========== ^^^

            ILGenerator il = dm.GetILGenerator();

            // Get the value of the first object's property.
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, getMethod, null);
            // Box the value type
            il.Emit(OpCodes.Box, pi.PropertyType);

            // Get the value of the second object's property.
            il.Emit(OpCodes.Ldarg_1);
            il.EmitCall(OpCodes.Call, getMethod, null);
            // Box the value type
            il.Emit(OpCodes.Box, pi.PropertyType);

            // Cast the first value to IComparable and call CompareTo,
            // passing the second value as the argument.
            il.Emit(OpCodes.Castclass, typeof(IComparable));
            il.EmitCall(OpCodes.Call, typeof(IComparable).GetMethod("CompareTo"), null);

            // If descending then multiply comparison result by -1
            // to reverse the ordering.
            if (direction == ListSortDirection.Descending)
            {
                il.Emit(OpCodes.Ldc_I4_M1);
                il.Emit(OpCodes.Mul);
            }

            // Return the result of the comparison.
            il.Emit(OpCodes.Ret);

            // Create the delegate pointing at the dynamic method.
            return (Comparison<T>)dm.CreateDelegate(typeof(Comparison<T>));
        }

3 个答案:

答案 0 :(得分:8)

更新:我终于以正确的方式工作了,请看下面的内容。

DynamicMethod在运行时动态构建方法;在库中,您正在使用创建的方法添加到对象T;但是,当T是一个接口时,这会失败,因为您无法向接口添加方法。

主要问题在于方法:

private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)

作为编写它的方式,只有在集合类型T具体时才会起作用。

如果您将实施更改为:

private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)
{
    MethodInfo getMethod = pi.GetGetMethod();
    Debug.Assert(getMethod != null);

    DynamicMethod dm = new DynamicMethod(
        "GetProperty_" + typeof(T).Name + "_" + pi.Name, typeof(object), 
        new Type[] { typeof(T) },
        pi.Module, 
        true);

    ILGenerator il = dm.GetILGenerator();

    il.Emit(OpCodes.Ldarg_0);
    il.EmitCall(OpCodes.Callvirt, getMethod, null);
    if (pi.PropertyType.IsValueType)
    {
        il.Emit(OpCodes.Box, pi.PropertyType);
    }

    // Return the result of the comparison.
    il.Emit(OpCodes.Ret);

    return (GetPropertyDelegate)dm.CreateDelegate(typeof(GetPropertyDelegate));
}

it will work for both concrete types and interfaces

您还需要更新以下两种方法:

private static Comparison<T> BuildValueTypeComparison(PropertyInfo pi, ListSortDirection direction)

private static Comparison<T> BuildNullableComparison(PropertyInfo pi, ListSortDirection direction)

我可能错了,但我认为在这些方法中实现的速度增益来自快速属性读取,因此使用DynamicMethod方法编写整个方法并没有太大的好处;我们可以重复使用上面的BuildGetPropertyMethod。这样做,这些变成了:

private static Comparison<T> BuildValueTypeComparison(
    PropertyInfo pi, 
    ListSortDirection direction)
{
    GetPropertyDelegate m = BuildGetPropertyMethod(pi);
    Comparison<T> d = delegate(T x, T y)
    {
        object mx = m(x);
        object my = m(y);

        IComparable c = (IComparable)mx;

        if (direction == ListSortDirection.Descending)
        {
            return -c.CompareTo(my);
        }

        return c.CompareTo(my);
    };

    return d;
}

private static Comparison<T> BuildNullableComparison(
    PropertyInfo pi, 
    ListSortDirection direction)
{
    GetPropertyDelegate m = BuildGetPropertyMethod(pi);
    Comparison<T> d = delegate(T x, T y)
        {
            object mx = m(x);
            object my = m(y);

            IComparable c = (IComparable)mx;

            if (c == null)
            {
                c = (IComparable)my;

                if (c == null)
                {
                    return 0;
                }

                return direction == ListSortDirection.Descending 
                    ? c.CompareTo(mx) : -c.CompareTo(mx);
            }

            return direction == ListSortDirection.Descending 
                ? -c.CompareTo(my) : c.CompareTo(my);
        };

    return d;
}

显然对它做了一些测试,但我很确定这就是你想要的,它应该与前面的代码一样快。

答案 1 :(得分:2)

不要放弃并继续使用DataSet!你正朝着正确的方向前进!现在,我们先来看一下函数签名:

DynamicMethod(string name, 
              Type returnType, 
              Type[] parameterTypes, 
              Type owner, 
              bool skipVisibility)

Error is “Invalid type owner for DynamicMethod"

错误消息试图告诉您,Type owner不是函数所期望的。它想要一个Class类型。您可能正在将接口类型传递到类型所有者。在接口上创建动态方法是不可能的。

1.错误示例

也许您正在使用依赖注入,并且您可能希望使用Interface。

但是,此代码将遇到运行时错误。

var viewModelList = GetViewModels(); //returns an IList<IViewModel> <-- has i !!
var blv = new BindingListView<IViewModel>(viewModelList);

2.工作示例

尝试重新设计代码以使用Concrete Type。

现在,此代码不会遇到运行时错误

var viewModelList = GetViewModels(); //returns an IList<ViewModel> <-- has no i !!
var blv = new BindingListView<ViewModel>(viewModelList);

然后,DataGridView上的排序和过滤将自动运行:)

EDIT --------------------------------------------- < / p>

P.S。关于尝试重新设计代码:

如果您使用的是MVVM / MVP模式,请考虑以下逻辑。 IList<IViewModel>应保留在“VM + P”端。使用IViewModel的目的主要是因为我希望能够用MockingViewModel : IViewModel替换它来对单元测试逻辑的“VM + P”进行单元测试。

现在,BindingListView<ViewModel>应该在“V”方面,即YourView : System.Windows.Form { ... }。它将绑定到绑定源YourBindingSource.DataSource = blv;由于我不会对WinForm进行单元测试,因此我将尝试将它们重构为演示者和视图模型,并尽可能保持View的精简。所以,我只是在BindingListView中使用ViewModel,而不是接口IViewModel。

所以BindingListView<ConcreteViewModel>对我来说自然有意义,它不接受模型界面。

请参阅有关MVVM MVP设计和单元测试WinForm的这个问题: Should I unit-test my view in MVP(or VM) or how to keep the code in the view to a minimum?

答案 2 :(得分:-1)

为什么他们为属性创建新方法,为什么?他们不能只使用PropertyInfo并获取属性值吗?如果我这样做,我会考虑接口,而不是限制用户使用它们。他们正在创造同样的方法,称为原始&#34;得到&#34;方法,我不明白这是什么意思。

 private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)
        {
            MethodInfo getMethod = pi.GetGetMethod();
            Debug.Assert(getMethod != null);

            DynamicMethod dm = new DynamicMethod("__blw_get_" + pi.Name, typeof(object), new Type[] { typeof(T) }, typeof(T), true);
            ILGenerator il = dm.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, getMethod, null);

            // Return the result of the comparison.
            il.Emit(OpCodes.Ret);

            // Create the delegate pointing at the dynamic method.
            return (GetPropertyDelegate)dm.CreateDelegate(typeof(GetPropertyDelegate));
        }

修正了排序:

            private static Comparison<T> BuildComparison(string propertyName, ListSortDirection direction)
        {
            PropertyInfo pi = typeof(T).GetProperty(propertyName);
            Debug.Assert(pi != null, string.Format("Property '{0}' is not a member of type '{1}'", propertyName, typeof(T).FullName));

            if (typeof(IComparable).IsAssignableFrom(pi.PropertyType))
            {
                if (pi.PropertyType.IsValueType)
                {
                    return BuildValueTypeComparison(pi, direction);
                }
                else
                {
                    //CHANGED!!!!!
                    //GetPropertyDelegate getProperty = BuildGetPropertyMethod(pi);
                    return delegate(T x, T y)
                    {
                        int result;
                        //CHANGED!!!!!
                        object value1 = pi.GetValue(x, null);// getProperty(x);
                        //CHANGED!!!!!
                        object value2 = pi.GetValue(y, null); //getProperty(y);
                        if (value1 != null && value2 != null)
                        {
                            result = (value1 as IComparable).CompareTo(value2);
                        }
                        else if (value1 == null && value2 != null)
                        {
                            result = -1;
                        }
                        else if (value1 != null && value2 == null)
                        {
                            result = 1;
                        }
                        else
                        {
                            result = 0;
                        }

                        if (direction == ListSortDirection.Descending)
                        {
                            result *= -1;
                        }
                        return result;
                    };
                }
            }
            else if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                var compare = typeof(Nullable).GetMethod("Compare", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(pi.PropertyType.GetGenericArguments()[0]);
                return delegate (T x, T y)
                {
                    return (int)compare.Invoke(x,new[] { pi.GetValue(x, null), pi.GetValue(y, null) } );
                };
                //return BuildNullableComparison(pi, direction);
            }
            else
            {
                return delegate(T o1, T o2)
                {
                    if (o1.Equals(o2))
                    {
                        return 0;
                    }
                    else
                    {
                        return o1.ToString().CompareTo(o2.ToString());
                    }
                };
            }
        }