Is it possible in C# (I'm using .Net 4.5, but asking more generally) to determine if two implementations of a generic type are functionally equivalent?
As an example of the need, suppose I have an IMapper
interface defined as:
public interface IMapper<TTo, TFrom>
{
TTo MapFrom(TFrom obj);
TFrom MapFrom(TTo obj);
}
My ideal implementation of this is:
public class MyMapper : IMapper <A, B>
{
A MapFrom(B obj) {...}
B MapFrom(A obj) {...}
}
This would be functionally equivalent to:
public class MyEquivalentMapper : IMapper <B, A>
{
B MapFrom(A obj) {...}
A MapFrom(B obj) {...}
}
but the compiler (rightly) recognizes these as different types. Is there a way I can tell the compiler to treat these two types as equivalent (and perhaps even interchangeable)?
I've also looked at this:
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) {...}
}
but I found that I can't properly identify an abstraction so that I can inject (into constructors, etc.) the concrete class without creating a "middle-man" interface:
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) {...}
}
I think the "middle-man" approach is more "correct", but I'd prefer not to create a superfluous type. Also, it still carries the problem where swapping the type arguments creates two different, yet functionally equivalent, types.
Is there a better way to achieve my goal?
Given this definition:
public interface IMapper<out TTo, in TFrom>
{
TTo MapFrom(TFrom obj);
TFrom MapFrom(TTo obj);
}
The types IMapper<A, B>
and IMapper<B, A>
are actually not equivalent because of the asymmetric covariant/contravariant generic parameters. But, ignoring that...
You could try something like the following (although this might have problems when A and B have the same type).
//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>
{
}
Now, for example, you can define an IUndirectedMapper<int, string>
some place in your code and then use it as both a IBidirectionalMapper<int, string>
and a IBidirectionalMapper<string, int>
.
These definitions give you three errors of the following flavor.
IBidirectionalMapper<A,B>
cannot implement both IDirectionalMapper<A,B>
and IDirectionalMapper<B,A>
because they may unify for some type parameter substitutions
Looks like this approach doesn't work, sorry.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With