2012-05-22 22 views
10

Stiamo utilizzando Andrew Davey'sBindingListView<T> classe tramite sourceforge per legare le collezioni ad un DataGridView e consentire l'ordinamento e filtraggio.errore "tipo proprietario non valido per DynamicMethod" durante l'ordinamento un'interfaccia

Questo funziona bene per le collezioni normali. In un caso però la collezione stiamo legame è un tipo di interfaccia e si ottiene questo errore se si cerca di ordinare su di esso:

Invalid type owner for DynamicMethod

L'errore è nel profondo del codice di Andrew Davies', quindi è difficile per noi per sapere da dove cominciare.

 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

Ho appena scaricato la biblioteca, e modificato l'applicazione di esempio in modo che sia vincolante per un'interfaccia piuttosto che un tipo concreto e funziona benissimo; quindi non può essere solo il fatto che tu sia vincolante per un'interfaccia. Puoi pubblicare un'interfaccia minima e implementare una classe che mostri questo comportamento perché questo è sicuramente specifico per l'implementazione? – briantyler

+0

OK. Riporto, si sta utilizzando 'AggregateBindingListView' e si lancia. – briantyler

risposta

2

Non rinunciare e andare a utilizzare DataSet! Stai andando nella giusta direzione! Ora, si deve prima prendere uno sguardo alla firma della funzione:

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

Error is “Invalid type owner for DynamicMethod" 

Il messaggio di errore sta cercando di dirti, Type owner non è quello che si aspetta la funzione. Vuole un tipo di classe. Probabilmente stai passando un tipo di interfaccia al proprietario del tipo . È impossibile creare metodi dinamici su un'interfaccia.

1.Error Esempio

Forse si utilizza Dependency Injection, e come si può usare l'interfaccia.

Tuttavia, questo codice verrà visualizzato Errore di runtime.

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

2.Working Esempio

Prova a ridisegnare il codice per utilizzare tipo concreto.

Ora, questo codice non ha colpito Runtime Error

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

Quindi, il vostro ordinamento e filtraggio su DataGridView lavoreranno automagicamente :)

EDIT ------------ ---------------------------------

PS Informazioni su come riprogettare il codice:

Se si utilizza un pattern MVVM/MVP, considerare la seguente logica. Lo IList<IViewModel> dovrebbe rimanere sul lato "VM + P". Lo scopo dell'utilizzo di IViewModel è principalmente perché voglio essere in grado di sostituirlo con, diciamo, MockingViewModel : IViewModel per l'unità testando il lato "VM + P" delle logiche.

Ora, lo BindingListView<ViewModel> dovrebbe essere realmente sul lato "V", ovvero lo YourView : System.Windows.Form { ... }. E sarà collegato alla sorgente di binding da lì YourBindingSource.DataSource = blv; Dato che non eseguirò un test di unità su WinForm, qualsiasi logica al suo interno proverò a rifattorizzarli in relatori e viewmodels e manterrò la Vista il più sottile possibile. Quindi, vorrei solo utilizzare ViewModel in BindingListView, non l'interfaccia IViewModel.

Quindi il BindingListView<ConcreteViewModel> avrà naturalmente senso per me che non accetta un'interfaccia modello.

fare riferimento a questa domanda su MVVM MVP progettazione e Unit Testing WinForm: Should I unit-test my view in MVP(or VM) or how to keep the code in the view to a minimum?

7

UPDATE: Ho finalmente ottenuto questo lavoro nel modo giusto, vedere qui di seguito.

DynamicMethod crea dinamicamente un metodo in fase di esecuzione; nella libreria che stai usando il metodo che viene creato viene aggiunto all'oggetto T; tuttavia, quando l'interfaccia T non funziona, non è possibile aggiungere un metodo a un'interfaccia.

il problema principale è nel metodo:

private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)

come il modo in cui questo è stato scritto che funziona solo quando il tipo di raccolta T è concreto.

Se si modifica l'applicazione 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)); 
} 

it will work for both concrete types and interfaces.

È inoltre necessario aggiornare i seguenti due metodi:

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

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

potrei sbagliarmi, ma penso che il guadagno velocità raggiunta in questi metodi deriva dalla proprietà di lettura veloce , quindi non c'è davvero tanto vantaggio dalla scrittura di tutti i metodi usando il metodo DynamicMethod; possiamo semplicemente riutilizzare lo BuildGetPropertyMethod dall'alto. Così facendo, questi diventano:

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

Ovviamente fare qualche test su di esso, ma sono abbastanza sicuro che è quello che vuoi e che dovrebbe essere di circa veloce come il codice precedente.

+0

Fantastico. Ho inserito il codice nel mio progetto di prova e l'ho provato. Esso funziona magicamente. – Tom

+0

@ Tom - evviva :) – briantyler

-1

Perché stanno creando un nuovo metodo per le proprietà, perché? Non potrebbero semplicemente usare quel PropertyInfo e ottenere il valore della proprietà? Se lo facessi, avrei in mente le interfacce e non impedisco agli utenti di usarle. Stanno creando lo stesso tipo di metodo che chiama il metodo "get" originale, non capisco qual è il punto di questo.

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

fisso per l'ordinamento:

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