2012-05-22 40 views
10

Andrew Davey'sBindingListView<T>クラスを使用してsourceforgeを使用してコレクションをDataGridViewにバインドし、並べ替えとフィルタリングが可能です。「DynamicMethodのタイプ所有者が無効です」というエラーが発生しました。

通常のコレクションでは問題ありません。あるケースではしかし、我々はに結合しているコレクションは、インターフェイスのタイプであり、我々はそれをソートしようとした場合、私たちは、このエラーを取得:

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

を私はライブラリをダウンロードし、それはインターフェースではなく、具体的な形に結合され、それが正常に動作するように、サンプル・アプリケーションを変更しました。インターフェイスにバインドしているだけではありません。これは実装固有のものなので、最小限のインターフェイスを実装し、この動作を示すクラスを実装できますか? – briantyler

+0

私はそれを取り戻し、あなたは 'AggregateBindingListView'を使用しています。 – briantyler

答えて

2

DataSetをあきらめて使用しないでください!あなたは正しい方向に向かっています!それでは、最初の関数のシグネチャを見てみましょう:

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

Error is “Invalid type owner for DynamicMethod" 

エラーメッセージはあなたを教えしようとしている、Type ownerは、関数が期待しているものではありません。クラスタイプが必要です。あなたはおそらくタイプのオーナーにインターフェイスタイプを渡しています。インターフェイス上に動的メソッドを作成することは不可能です。

1.Error例

たぶん、あなたは、依存性注入を使用している、とあなたは、インターフェイスを使用したいことがあります。

ただし、このコードではランタイムエラーが発生します。

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

2.Working例

コンクリートタイプを使用するようにコードを再設計してください。

さて、このコードは次にランタイムエラー

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

をヒットしないだろう、DataGridView上のあなたのソートとフィルタリングが自動的に動作します:)

EDITを------------ ---------------------------------

PS

MVVM/MVPパターンを使用している場合は、次のロジックを考慮してください。 IList<IViewModel>は「VM + P」側に留まる必要があります。 IViewModelを使用する目的は、ロジックの「VM + P」側をユニットテストするために、たとえばMockingViewModel : IViewModelと置き換えることができるためです。

BindingListView<ViewModel>は、実際には「V」側、つまりYourView : System.Windows.Form { ... }になります。そこからバインドされたソースにバインドされますYourBindingSource.DataSource = blv; WinFormの単体テストではないので、その中のロジックをプレゼンターとビューモデルにリファクタリングして、Viewをできるだけ薄くします。だから、私はちょうどBindingListViewではなく、インターフェイスIViewModel ViewModelを使用します。

したがって、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?

7

UPDATE:私は最終的にこの作業を正しい方法を持って、下記を参照してください。

DynamicMethodは、実行時に動的にメソッドを構築します。ライブラリ内に作成されたメソッドを使用している場合は、オブジェクトに追加されていますT;ただし、Tがインターフェイスの場合、インターフェイスにメソッドを追加できないため、これは失敗します。

主な問題は、この方法である:これはコレクション型Tはコンクリートがあるとき、それはのみ動作します書かれていた方法として

private static GetPropertyDelegate BuildGetPropertyMethod(PropertyInfo pi)

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

明らかにそれにいくつかのテストを行いますが、私はそれはあなたが望むものであるかなり確信していると、それは前のコードと同じくらい速い程度にする必要があります。

+0

素晴らしいです。あなたのコードをテストプロジェクトに入れて試しました。それは魅力のように働く。 – Tom

+0

@Tom - cheers :) – briantyler

-1

なぜ新しいメソッドを作成してプロパティを取得するのですか?なぜですか?彼らは単に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()); 
        } 
       }; 
      } 
     } 
関連する問題