Abstract use case:
// This works fine, but the second template parameter (T2) is redundant.
var correctSyntax = new T3<T1<T2>, T2>();
// This results in a syntax error, working as intended,
// but this kind of error is only possible because of redundancy (T4 have to match T2).
var incorrectSyntax = new T3<T1<T2>, T4>();
// Ideal syntax, that I could not achieve.
var idealSyntax = new T3<T1<T2>>();
More concrete use case:
Imagine the following, I have a class, that is supposed to take a collection, and store an index of it. The class moves the index forward or backward on demand, and takes care of cases, when the index is out of the bounds. The class has a property, that will return the value at the current index, which is ofcourse of type T.
Imagine, that the collection given to this class is a List, that I want to modify down the line. For this reason, I don't want to use IEnumerable in the class handling the collection, instead I want to store it with the concreate type, and make it public, so I can reach it like:
List<T> list = collectionIndexerInstance.Collection;
In this case, I could not find a better way to write the template constraints then this:
public class SelectedListItem<ListType, ItemType> where ListType: IList<ItemType>
{
public ListType Collection { get; set; }
public ItemType CurrentValue => Collection[currentIndex];
private int currentIndex;
...
}
This works fine, but when we declare or initialize the class it is reduntant and longer than ideal:
// This works fine, but the second template parameter is redundant.
var correctSyntax = new Example<List<int>, int>();
// This results in a syntax error, working as intended,
// but this kind of error is only possible because of redundancy.
var incorrectSyntax = new Example<List<int>, float>();
// Ideal syntax, that I could not achieve.
var idealSyntax = new Example<List<int>>();