确定类型等价

时间:2013-02-21 15:59:11

标签: c# types interface

是否有可能在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 个答案:

答案 0 :(得分: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>某个位置,然后将其用作IBidirectionalMapper<int, string>IBidirectionalMapper<string, int>

修改

这些定义为您提供以下风格的三个错误。

IBidirectionalMapper<A,B>无法实现IDirectionalMapper<A,B>IDirectionalMapper<B,A>,因为它们可能会针对某些类型参数替换进行统一

看起来这种方法不起作用,抱歉。

答案 1 :(得分:1)

你可以有一个映射器来完成两个翻译,但只是将它定向,因为在你的情况下总是有一个“我的对象”和“数据库对象”配对。

interface IDbTypeModel<T, TDb>
{
    T FromDb(TDb dbObj);
    TDb ToDb(T obj);
}

答案 2 :(得分:0)

没有办法表达IGenericInterface<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方法将绑定到输出“U to T”的方法,即使在MyClass2<Foo,Foo>这两种类型都相同的情况下也是如此。

答案 3 :(得分: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());
    }