Error "Propietario de tipo no válido para DynamicMethod" al ordenar una interfaz

Estamos usando Andrew Davey BindingListView<T> clase vía forjado para vincular colecciones a un DataGridView y permitir la clasificación y el filtrado.

Esto funciona bien para colecciones normales. Sin embargo, en un caso, la colección a la que nos vinculamos es un tipo de interfaz y obtenemos este error si intentamos ordenarla:

Invalid type owner for DynamicMethod

El error está muy arraigado en el código de Andrew Davies, por lo que es difícil para nosotros saber por dónde empezar.

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

preguntado el 22 de mayo de 12 a las 10:05

Acabo de descargar la biblioteca y modifiqué la aplicación de muestra para que se vincule a una interfaz en lugar de a un tipo concreto y funcione bien; por lo tanto, no puede ser solo el hecho de que está vinculado a una interfaz. ¿Puede publicar una interfaz mínima y una clase de implementación que muestre este comportamiento porque definitivamente es una implementación específica? -

OK, retiro eso, estás usando el AggregateBindingListView y tira. -

3 Respuestas

ACTUALIZACIÓN: Finalmente tengo esto funcionando de la manera correcta, por favor vea a continuación.

DynamicMethod construye un método dinámicamente en tiempo de ejecución; en la biblioteca que está utilizando, el método que se crea se agrega al objeto T; sin embargo cuando T es una interfaz que falla porque no puede agregar un método a una interfaz.

El principal problema está en el método:

private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)

tal como se escribió esto, solo funcionará cuando el tipo de colección T es concreto

Si cambia la implementación a:

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

funcionará tanto para tipos concretos como para interfaces.

También debe actualizar los dos métodos siguientes:

private static Comparison<T> BuildValueTypeComparison(PropertyInfo pi, ListSortDirection direction)

private static Comparison<T> BuildNullableComparison(PropertyInfo pi, ListSortDirection direction)

Puede que me equivoque, pero creo que la ganancia de velocidad lograda en estos métodos proviene de la lectura rápida de propiedades, por lo que realmente no hay mucho beneficio al escribir todos los métodos usando el DynamicMethod método; podemos simplemente reutilizar el BuildGetPropertyMethod desde arriba. Así que al hacer esto, estos se convierten en:

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

Obviamente, haz algunas pruebas, pero estoy bastante seguro de que eso es lo que quieres y debería ser tan rápido como el código anterior.

Respondido el 13 de Septiembre de 17 a las 10:09

Impresionante. Puse su código en mi proyecto de prueba y lo probé. Funciona a las mil maravillas. - tom

¡No te rindas y ve a usar DataSet! ¡Estás en la dirección correcta! Ahora, primero echemos un vistazo a la firma de la función:

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

Error is “Invalid type owner for DynamicMethod"

El mensaje de error está tratando de decirle, Type owner no es lo que espera la función. Quiere un tipo de clase. Probablemente esté pasando un tipo de interfaz al Propietario del tipo. Es imposible crear métodos dinámicos en una interfaz.

1.Ejemplo de error

Tal vez esté usando Inyección de dependencia y le gustaría usar Interfaz.

Sin embargo, este código generará un error de tiempo de ejecución.

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

2.Ejemplo de trabajo

Intente rediseñar su código para usar Concrete Type.

Ahora, este código NO alcanzará el error de tiempo de ejecución

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

Luego, su clasificación y filtrado en DataGridView funcionará automáticamente :)

EDITAR ---------------------------------------------

PD: sobre intentar rediseñar tu código:

Si está utilizando un patrón MVVM/MVP, piense en la siguiente lógica. El IList<IViewModel> debe permanecer en el lado "VM+P". El propósito de usar IViewModel es principalmente porque quiero poder reemplazarlo con, digamos, MockingViewModel : IViewModel para pruebas unitarias del lado "VM+P" de las lógicas.

Ahora, la BindingListView<ViewModel> debería estar realmente en el lado "V", es decir, el YourView : System.Windows.Form { ... }. Y se vinculará a la fuente de vinculación desde allí. YourBindingSource.DataSource = blv; Dado que no haré pruebas unitarias de un WinForm, cualquier lógica en él intentaré refactorizarlos en presentadores y modelos de vista y mantener la vista lo más delgada posible. Entonces, solo usaría ViewModel en BindingListView, no la interfaz IViewModel.

Entonces el BindingListView<ConcreteViewModel> Naturalmente, tendrá sentido para mí que no acepte una interfaz modelo.

Consulte esta pregunta sobre el diseño de MVVM MVP y las pruebas unitarias de WinForm: ¿Debo realizar una prueba unitaria de mi vista en MVP (o VM) o cómo mantener el código en la vista al mínimo?

contestado el 23 de mayo de 17 a las 13:05

¿Por qué están creando un nuevo método para obtener propiedades, por qué? ¿No podrían simplemente usar ese PropertyInfo y obtener el valor de la propiedad? Si estuviera haciendo esto, tendría en mente las interfaces y no restringiría el uso de las mismas por parte de los usuarios. Están creando el mismo tipo de método que llama al método "obtener" original, no entiendo cuál es el punto de esto.

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

Fijo para clasificar:

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

Respondido 22 Oct 17, 21:10

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.