我们通过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>));
}
答案 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());
}
};
}
}