是否有可能在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) {...}
}
我认为“中间人”方法更“正确”,但我宁愿不创造多余的类型。此外,它仍然存在交换类型参数创建两个不同但功能相当的类型的问题。
有没有更好的方法来实现我的目标?
答案 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());
}