Ошибка "Недопустимый тип владельца для DynamicMethod" при сортировке интерфейса
Мы используем класс BindingListView<T>
Эндрю Дэйви через sourceforge, чтобы привязать коллекции к 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 the line reporting the error=========== ^^^
ILGenerator il = dm.GetILGenerator();
// Get the value of the first object 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 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>));
}
Ответы
Ответ 1
UPDATE: Я, наконец, получил правильную работу, см. ниже.
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;
}
Очевидно, сделайте некоторое тестирование на нем, но я уверен, что это то, что вы хотите, и должно быть примерно так же быстро, как и предыдущий код.
Ответ 2
Не сдавайтесь и не используйте DataSet! Вы направляетесь в правильном направлении! Теперь давайте сначала взглянем на подпись функции:
DynamicMethod(string name,
Type returnType,
Type[] parameterTypes,
Type owner,
bool skipVisibility)
Error is "Invalid type owner for DynamicMethod"
Сообщение об ошибке пытается сказать вам, Type owner
- это не то, что ожидает функция. Он хочет тип класса. Вероятно, вы передаете тип интерфейса в владелец Тип. Невозможно создать динамические методы в интерфейсе.
Пример 1.Error
Возможно, вы используете Dependency Injection, и вы можете использовать интерфейс.
Однако этот код ударит Runtime Error.
var viewModelList = GetViewModels(); //returns an IList<IViewModel> <-- has i !!
var blv = new BindingListView<IViewModel>(viewModelList);
Пример 2.Working
Попробуйте изменить свой код на использование типа бетона.
Теперь этот код НЕ будет удалять Runtime Error
var viewModelList = GetViewModels(); //returns an IList<ViewModel> <-- has no i !!
var blv = new BindingListView<ViewModel>(viewModelList);
Затем ваша сортировка и фильтрация на DataGridView
будет работать автоматически:)
ИЗМЕНИТЬ ---------------------------------------------
P.S. О попытке переделать код:
Если вы используете шаблон MVVM/MVP, подумайте о следующей логике. IList<IViewModel>
должен оставаться на стороне "VM + P". Цель использования IViewModel заключается, главным образом, в том, что я хочу заменить его, скажем, MockingViewModel : IViewModel
для модульного тестирования логики "VM + P".
Теперь BindingListView<ViewModel>
должен действительно находиться на стороне "V", то есть YourView : System.Windows.Form { ... }
. И он будет привязан к источнику привязки оттуда YourBindingSource.DataSource = blv;
Так как я не буду тестировать WinForm, любая логика в нем я попытаюсь реорганизовать их в презентаторы и viewmodels и сохранить представление как можно более тонким. Итак, я бы просто использовал ViewModel в BindingListView, а не интерфейс IViewModel.
Итак, BindingListView<ConcreteViewModel>
, естественно, будет иметь для меня смысл, что он не принимает модельный интерфейс.
Обратитесь к этому вопросу о проектировании MVVM MVP и модульном тестировании WinForm:
Должен ли я выполнить модульное тестирование моего представления в MVP (или виртуальной машине) или как сохранить код в представлении до минимума?
Ответ 3
Почему они создают новый метод для свойств, почему? Не могли бы они просто использовать эту 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());
}
};
}
}