'Restricting T to string and int?

I have built myself a generic collection class which is defined like this.

public class StatisticItemHits<T>{...}

This class can be used with int and string values only. However this

public class StatisticItemHits<T> where T : string, int {...}

won't compile. What am I doing wrong?



Solution 1:[1]

You could make StatisticItemHits<T> an abstract class and create two subclasses:

StatisticItemHitsInt : StatisticItemHits<int>{}

StatisticItemHitsString : StatisticItemHits<string>{}

That way there can only be an int and string-representation of StatisticItemHits

Solution 2:[2]

As others have said, you cannot use type restrictions in this way. What you can do is to specialise from your base type as follows:

public class StatisticItemHits <T> { }

public class StringStatisticItemHits : StatisticItemHits<string> { }

public class IntegerStatisticItemHits : StatisticItemHits<int> { }

Also, as your base class is generic, you won't be able to use this to polymorphically. If you need to do this make StatisticItemHits implement an interface and use this to reference instances. Something like:

public class StatisticItemHits <T> : IStaticticItemHits { }

public interface IStatisticItemHits { }

Solution 3:[3]

You cannot restrict it to string and int from the where clause. You can check it in the constructor, but that is probably not a good place to be checking. My approach would be to specialize the class and abstract the class creation into a (semi-)factory pattern:

class MyRestrictedGeneric<T>
{
    protected MyRestrictedGeneric() { }


    // Create the right class depending on type T
    public static MyRestrictedGeneric<T> Create<T>()
    {
        if (typeof(T) == typeof(string))
            return new StringImpl() as MyRestrictedGeneric<T>;

        if (typeof(T) == typeof(int))
            return new IntImpl() as MyRestrictedGeneric<T>;

        throw new InvalidOperationException("Type not supported");
    }


    // The specialized implementation are protected away
    protected class StringImpl : MyRestrictedGeneric<string> { }
    protected class IntImpl : MyRestrictedGeneric<int> { }
}

This way you can limit the class's usage to just string and int internally inside your class.

Solution 4:[4]

Given that you only have two types here I would go down an OO route here instead and just have two classes for the two types.

Generics are best used where the circumstances in which they can be applied are, you know, generic. They're a lot less use in circumstances like this.

You can restrict to struct or class types only, and I do think that there need to be numeric or operator based restrictions (e.g. must support +=)

Int and string are really quite different, certainly more different than int and double. It wouldn't make much sense for a generic class to support the immutable reference type of string and the value type of int without also supporting other types more similar to either of them.

Solution 5:[5]

Not possible to do this with 'where'. You can't do an 'or'.

Solution 6:[6]

Simply use:

where TSource : IEquatable<string>

Solution 7:[7]

Checking this: https://riptutorial.com/csharp/example/8137/where

hybrid value/reference type

Occasionally it is desired to restrict type arguments to those available in a database, and these will usually map to value types and strings. As all type restrictions must be met, it is not possible to specify where T : struct or string (this is not valid syntax). A workaround is to restrict type arguments to IConvertible which has built in types of "... Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char, and String." ...

public class Cup<T> where T : IConvertible
{
    // ...
}

I solved the same problem you have with this solution, see my working example here:

static void Main()
{
    var r1 = TryTest<int>(123); //Unmanaged supported
    var r2 = TryTest<DateTime>(DateTime.Now); //Unmanaged supported
    var r3 = TryTest<double>(123.55); //Unmanaged supported
    var r4 = TryTest<string>("Homer Simpson"); //String supported
}

public static List<T> TryTest<T>(T someValue) where T : IConvertible
{
    var list = new List<T>(); // Just for the sake of the example, a list of <T>
    list.Add(someValue); //Add it...
    return list;
}

The result will be a List of each type, string included. (Use watch to confirm)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ruben
Solution 2 Sean Kearon
Solution 3
Solution 4 Keith
Solution 5 tuinstoel
Solution 6 newfurniturey
Solution 7 Yogurtu