12

我们通过sourceforge使用Andrew Davey 的 BindingListView<T>类将集合绑定到 a并允许排序和过滤。 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>));
        }
4

3 回答 3

9

更新:我终于以正确的方式工作了,请参见下文。

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

它适用于具体类型和接口

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

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

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

于 2012-12-09T18:33:55.557 回答
3

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

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

Error is “Invalid type owner for DynamicMethod"

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

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将自动工作:)

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

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

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

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

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

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

于 2012-12-04T04:58:54.740 回答
-1

他们为什么要为属性创建一个新方法 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());
                    }
                };
            }
        }
于 2017-10-22T14:16:30.060 回答