12

sourceforgeを介してAndrew Davey の BindingListView<T>クラスを使用して、コレクションを にバインドし、並べ替えとフィルタリングを許可しています。 DataGridView

これは、通常のコレクションでは問題なく機能します。ただし、あるケースでは、バインドしているコレクションが Interface 型であり、それを並べ替えようとすると、次のエラーが発生します。

Invalid type owner for DynamicMethod

エラーは Andrew Davies のコードの奥深くにあるため、どこから始めればよいかわかりません。

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

3 に答える 3

9

更新:ようやくこれが正しく機能するようになりました。以下を参照してください。

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

具体的な型とインターフェイスの両方で機能します

次の 2 つのメソッドも更新する必要があります。

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

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

于 2012-12-09T18:33:55.557 に答える
3

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

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

Error is “Invalid type owner for DynamicMethod"

エラー メッセージが伝えようとしてType ownerいるのは、関数が予期しているものではありません。クラス型が必要です。おそらく、 Interface タイプをType ownerに渡しています。インターフェイスで動的メソッドを作成することはできません。

1.エラー例

依存性注入を使用していて、インターフェイスを使用したい場合があります。

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

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が自動的に機能します:)

編集 - - - - - - - - - - - - - - - - - - - - - - -

PSコードを再設計しようとすることについて:

MVVM/MVP パターンを使用している場合は、次のロジックについて考えてください。はIList<IViewModel>「VM+P」側に留まる必要があります。IViewModel を使用する主な目的はMockingViewModel : IViewModel、ロジックの "VM+P" 側の単体テストなどに置き換えたいからです。

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

そのBindingListView<ConcreteViewModel>ため、モデル インターフェイスを受け入れないことは当然のことです。

MVVM MVP の設計と単体テストの WinForm に関するこの質問を参照してください

于 2012-12-04T04:58:54.740 に答える