【问题标题】:"Invalid type owner for DynamicMethod" error when sorting an Interface对接口进行排序时出现“DynamicMethod 的类型所有者无效”错误
【发布时间】:2012-05-22 09:05:16
【问题描述】:

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

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

Invalid type owner for DynamicMethod

这个错误在 Andrew Davies 的代码中很深,所以我们很难知道从哪里开始。

        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>));
        }

【问题讨论】:

  • 我刚刚下载了库,并修改了示例应用程序,使其绑定到接口而不是具体类型,并且工作正常;所以它不能只是你绑定到一个接口的事实。您能否发布一个展示此行为的最小接口和实现类,因为这绝对是特定于实现的?
  • 好的,我收回,你正在使用AggregateBindingListView,它确实抛出了。

标签: .net sorting interface


【解决方案1】:

更新:我终于找到了正确的方法,请看下文。

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&lt;T&gt; BuildValueTypeComparison(PropertyInfo pi, ListSortDirection direction)

private static Comparison&lt;T&gt; 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;
}

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

【讨论】:

  • 太棒了。我将您的代码放在我的测试项目中并进行了尝试。它就像一个魅力。
【解决方案2】:

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

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

Error is “Invalid type owner for DynamicMethod"

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

1.错误示例

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

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

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

2.工作示例

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

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

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

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

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

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

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

现在,BindingListView&lt;ViewModel&gt; 应该真正位于“V”侧,即YourView : System.Windows.Form { ... }。并且它将从那里绑定到绑定源YourBindingSource.DataSource = blv; 因为我不会对 WinForm 进行单元测试,所以我将尝试将它们重构为演示者和视图模型,并使视图尽可能薄。所以,我只会在 BindingListView 中使用 ViewModel,而不是 IViewModel 接口。

所以BindingListView&lt;ConcreteViewModel&gt; 不接受模型接口对我来说自然是有意义的。

参考这个关于 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?

【讨论】:

    【解决方案3】:

    他们为什么要为属性创建一个新方法 get,为什么?他们不能只使用该 PropertyInfo 并获取属性值吗?如果我这样做,我会考虑界面而不限制用户使用它们。他们正在创建与原始“get”方法相同的方法,我不明白这是什么意思。

     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());
                        }
                    };
                }
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多