General class with two unequal (unique) types

Is it possible to implement a class limited by two unique parameters?

If this is not so, is it because it is not implemented or because it would be impossible to get the structure of the language (inheritance)?

I would like something like a form:

class BidirectionalMap<T1,T2> where T1 != T2 { ... } 

I am implementing a bi-directional dictionary . This is mainly a matter of curiosity, not necessity.


Paraphrased from the comments:

  • Dan: "What is the negative consequence if this restriction is not met?"

  • Me: "Then the user could index the map [t1] and map [t2]. If they were of the same type, there would be no difference, and that would not make sense."

  • Dan: the compiler actually allows [two types of type parameters to define various method overloads], so I'm curious; randomly selects one of the calling methods?

+7
source share
6 answers

Turning around with an example to highlight the problem:

 public class BidirectionalMap<T1,T2> { public void Remove(T1 item) {} public void Remove(T2 item) {} public static void Test() { //This line compiles var possiblyBad = new BidirectionalMap<string, string>(); //This causes the compiler to fail with an ambiguous invocation possiblyBad.Remove("Something"); } } 

So, the answer is that even if you cannot specify the restriction T1! = T2, it does not matter, because the compiler will fail as soon as you try to do something that violates the implicit restriction. It still catches a failure during compilation, so you can use these overloads with impunity. This is a bit strange since you can create a map instance (and even write IL code that manipulates the map accordingly), but the C # compiler will not allow you to damage the arbitrary resolution of ambiguous overloads.


The one-sided note is that such an overload can lead to some odd behavior if you are not careful. If you have a BidirectionalMap<Animal, Cat> and Cat: Animal, consider what happens with this code:

 Animal animal = new Cat(); map.Remove(animal); 

This will cause the overload that Animal accepts, so it will try to delete the key, even if you might have intended to delete the Cat value. This is a somewhat artificial case, but enough to warrant caution when very different behaviors occur as a result of method overloading. In such cases, it may be easier to read and maintain if you just give the methods different names that reflect their different behaviors (RemoveKey and RemoveValue, say.)

+4
source

Typical restrictions seem to be wrong. While they really prevent what is a type parameter, the goal is to let the compiler know what operations are available to the type.
If you wanted to, you might have a limitation in which T1 and T2 come from separate concrete base classes, but I don’t think this is what you want.

0
source

Wrong inequality will not help the compiler catch errors. When you specify restrictions on type parameters, you tell the compiler that variables of this type will always support a certain interface or will behave in a certain way. Each of them allows the compiler to check something more similar to "this method will be present so that it can be called on T".

Inequality of type parameters will be more like checking that the arguments of a method are not equal to zero. This is part of the program logic, not its security.

0
source

I'm not quite sure why this would be a desirable compile-time check. It would be possible to essentially circumvent the condition by boxing either the key or the value, thereby making the compile-time check useless.

Some attention needs to be taken to determine ... what errors am I trying to prevent?

If you just terminate the lazy employee due to lack of reading documentation, add only debug checking and throw an exception. Thus, the check can be removed for the release code, for example.

 #if Debug if (T1 is T2 || T2 is T1) { throw new ArguementException(...); } #endif 

If you are trying to prevent an attacker from using your library inadvertently, then you might need to check the runtime, otherwise it would be easy to put a key or value.

0
source

No, you cannot use equality (or inequality) as a limitation. Simply put, equality is not a limitation, but a condition. You must check for a condition such as equality or type inequality in the constructor, and throw an appropriate exception.

0
source

There is no effective way to do this without imposing any other restrictions on the types themselves. As someone else pointed out, you could make restrictions that the two types were derived from two different base classes, but that is probably not very good from a design point of view.

Edited to add: the reason this is not implemented is likely because no one at Microsoft has ever considered something like this necessary to enforce compilation time, unlike other restrictions that are related to how you are at actually capable of using variables of the specified types. And, as some commentators have noted, you can certainly ensure this at runtime.

-one
source

All Articles