7

是否可以在 C# 中(我使用的是 .Net 4.5,但更一般地询问)来确定泛型类型的两个实现是否在功能上等效?

作为需求的一个例子,假设我有一个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) {...}
}

但是编译器(正确地)将它们识别为不同的类型。有没有办法告诉编译器将这两种类型视为等效(甚至可以互换)?

我也看过这个:

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) {...}
}

我认为“中间人”方法更“正确”,但我不想创建一个多余的类型。此外,它仍然存在交换类型参数会创建两种不同但功能等效的类型的问题。

有没有更好的方法来实现我的目标?

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>

编辑

这些定义为您提供了以下三个错误。

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>应该被认为等同于a 的概念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方法将绑定到输出“U to T”的方法,即使在MyClass2<Foo,Foo>两个类型相同的情况下也是如此。

于 2013-02-21T21:04:33.170 回答
0

真的不清楚你所说的“等价”是什么意思。如果您的意思是两种类型是等效的,因为它们具有相同的成员并且成员具有相同的签名,那么您可以使用反射来确定:

    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 回答