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

haw*_*bsl 11 .net sorting interface

我们通过sourceforge使用Andrew Davey的 BindingListView<T>类来将集合绑定到a 并允许排序和过滤. 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>));
        }
Run Code Online (Sandbox Code Playgroud)

bri*_*ler 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));
}
Run Code Online (Sandbox Code Playgroud)

它适用于具体的类型和接口.

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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


Tom*_*Tom 3

不要放弃,去使用DataSet吧!您正朝着正确的方向前进!现在,我们首先看一下函数签名:

\n\n
DynamicMethod(string name, \n              Type returnType, \n              Type[] parameterTypes, \n              Type owner, \n              bool skipVisibility)\n\nError is \xe2\x80\x9cInvalid type owner for DynamicMethod"\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

1.错误示例

\n\n

也许您正在使用依赖注入,并且您可能喜欢使用接口。

\n\n

然而,这段代码会遇到运行时错误。

\n\n
var viewModelList = GetViewModels(); //returns an IList<IViewModel> <-- has i !!\nvar blv = new BindingListView<IViewModel>(viewModelList);\n
Run Code Online (Sandbox Code Playgroud)\n\n

2.工作示例

\n\n

尝试重新设计代码以使用具体类型。

\n\n

现在,这段代码将不会遇到运行时错误

\n\n
var viewModelList = GetViewModels(); //returns an IList<ViewModel> <-- has no i !!\nvar blv = new BindingListView<ViewModel>(viewModelList);\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后,您的排序和过滤DataGridView将自动工作:)

\n\n

编辑 - - - - - - - - - - - - - - - - - - - - - - -

\n\n

PS 关于尝试重新设计您的代码:

\n\n

如果您使用 MVVM/MVP 模式,请考虑以下逻辑。应IList<IViewModel>留在“VM+P”一侧。使用 IViewModel 的目的主要是因为我希望能够将其替换为,比如说,MockingViewModel : IViewModel逻辑的“VM+P”侧的单元测试。

\n\n

现在,BindingListView<ViewModel>确实应该位于“V”侧,即YourView : System.Windows.Form { ... }. 它将从那里绑定到绑定源,YourBindingSource.DataSource = blv;因为我不会对 WinForm 进行单元测试,所以其中的任何逻辑我都会尝试将它们重构为演示者和视图模型,并保持视图尽可能薄。因此,我只会在 BindingListView 中使用 ViewModel,而不是接口 IViewModel。

\n\n

因此,BindingListView<ConcreteViewModel>它不接受模型接口对我来说自然是有意义的。

\n\n

请参阅有关 MVVM MVP 设计和 WinForm 单元测试的问题:\n我应该在 MVP(或 VM)中对我的视图进行单元测试还是如何将视图中的代码保持在最低限度?

\n