7

C#(.Net 4.5を使用していますが、より一般的に質問しています)で、ジェネリック型の2つの実装が機能的に同等であるかどうかを判断することは可能ですか?

必要性の例として、次のようにIMapper定義されたインターフェースがあるとします。

public interface IMapper<TTo, TFrom>
{
    TTo MapFrom(TFrom obj);
    TFrom MapFrom(TTo obj);
}

これの私の理想的な実装は次のとおりです。

public class MyMapper : IMapper <A, B>
{
    A MapFrom(B obj) {...}
    B MapFrom(A obj) {...}
}

これは、機能的には次のものと同等です。

public class MyEquivalentMapper : IMapper <B, A>
{
    B MapFrom(A obj) {...}
    A MapFrom(B obj) {...}
}

しかし、コンパイラは(正しく)これらを異なるタイプとして認識します。これらの2つのタイプを同等(そしておそらく交換可能)として扱うようにコンパイラーに指示する方法はありますか?

私もこれを見ました:

public interface ISingleMapper<out TTo, in TFrom>
{
    TTo MapFrom(TFrom obj);
}
public class MyAlternateMapper :
    ISingleMapper<A, B>,
    ISingleMapper<B, A>
{
    A MapFrom(B obj) {...}
    B MapFrom(A obj) {...}
}

しかし、「仲介者」インターフェースを作成せずに具象クラスを(コンストラクターなどに)注入できるように、抽象化を適切に識別できないことがわかりました。

public interface IBidirectionalMapper<TTo, TFrom> :
    ISingleMapper<TTo, TFrom>,
    ISingleMapper<TFrom, TTo>
{
    TTo MapFrom(TFrom obj);
    TFrom MapFrom(TTo obj);
}
public class MyAlternateMapper : IBidirectionalMapper<A, B>
{
    A MapFrom(B obj) {...}
    B MapFrom(A obj) {...}
}

「ミドルマン」のアプローチの方が「正しい」と思いますが、余計なタイプは作りたくないです。また、型引数を交換すると、2つの異なるが機能的に同等の型が作成されるという問題があります。

私の目標を達成するためのより良い方法はありますか?

4

4 に答える 4

1

この定義を考えると:

public interface IMapper<out TTo, in TFrom>
{
    TTo MapFrom(TFrom obj);
    TFrom MapFrom(TTo obj);
}

非対称の共変/反変のジェネリックパラメーターのため、タイプIMapper<A, B>IMapper<B, A>は実際には同等ではありません。しかし、それを無視して...

次のようなものを試すことができます(ただし、AとBが同じタイプの場合は問題が発生する可能性があります)。

//Represents an oriented one-way mapper
public interface IDirectionalMapper<A, B>
{
    B Map(A obj);
}

//Represents an oriented two-way mapper
public interface IBidirectionalMapper<A, B>
    : IDirectionalMapper<A, B>, IDirectionalMapper<B, A>
{
}

//Represents an unoriented two-way mapper
public interface IUndirectedMapper<A, B>
    : IBidirectionalMapper<A, B>, IBidirectionalMapper<B, A>
{
}

これで、たとえば、IUndirectedMapper<int, string>コード内のある場所を定義して、それをaIBidirectionalMapper<int, string>とaの両方として使用できますIBidirectionalMapper<string, int>

編集

これらの定義により、次のフレーバーの3つのエラーが発生します。

IBidirectionalMapper<A,B>IDirectionalMapper<A,B>両方を実装することはできません。またIDirectionalMapper<B,A>、一部のタイプパラメータ置換で統合される可能性があるためです。

申し訳ありませんが、このアプローチは機能しないようです。

于 2013-02-21T17:24:40.200 に答える
1

両方の変換を行う単一のマッパーを使用することもできますが、その場合は常に「マイオブジェクト」と「データベースオブジェクト」のペアが存在するため、方向付けを行うだけです。

interface IDbTypeModel<T, TDb>
{
    T FromDb(TDb dbObj);
    TDb ToDb(T obj);
}
于 2013-02-21T20:47:02.087 に答える
0

これらのインターフェースのメソッドは異なる方法でバインドされるため、 aIGenericInterface<T,U>をと同等と見なす必要があるという概念を表現する方法はありません。IGenericInterface<U,T>たとえば、そのようなインターフェイスを実装するクラスの場合

MyClass<T,U> : IGenericInterface<T,U>
{
  public T Convert(U param) { Console.WriteLine("U to T"); }
  public U Convert(T param) { Console.WriteLine("T to U"); }
}

呼び出される関数の選択は、TとUの役割によって異なります。次のようなことを行う場合:

MyClass2<T,U>
{

  ... inside some method
    T myT;
    U myU;
    IGenericInterface<T,U> myThing = new MyClass<T,U>();
     myT = myThing.Convert(myU);
}

上記のメソッドは、両方のタイプが同じでConvertあるようなものの中でも、「UtoT」を出力するメソッドにバインドされます。MyClass2<Foo,Foo>

于 2013-02-21T21:04:33.170 に答える
0

「同等性」とはどういう意味かはよくわかりません。2つのタイプが同じメンバーを持ち、メンバーが同じ署名を持っているために同等であることを意味する場合は、リフレクションを使用して次のことを決定できます。

    public class TypeComparer : IEqualityComparer<MemberInfo>
    {
        public bool Equals(MemberInfo x, MemberInfo y)
        {
            return x.ToString() == y.ToString();
        }

        public int GetHashCode(MemberInfo obj)
        {
            return obj.GetHashCode();
        }
    }

    public static bool AreTypesEqual(Type type1, Type type2)
    {
        return type1.GetMembers().
            SequenceEqual(type2.GetMembers(), new TypeComparer());
    }

メンバーの順序が重要でない場合:

    public static bool AreTypesEqual2(Type type1, Type type2)
    {
        return type1.GetMembers().OrderBy(e=>e.ToString()).
            SequenceEqual(type2.GetMembers().OrderBy(e=>e.ToString()), new TypeComparer());
    }
于 2013-02-21T21:18:20.193 に答える