Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A generic class which can take only certain types

Suppose, I want to create a generic class that can take only int and double as types.

public class A<T> where T: int, double
{
    public T property{get;set;}
}

For instance:

A<int> i = new A<int>();
i.property = 10;

A<double> d = new A<double>();
d.property = 0.01;

but, this is not working.

How can I do that?

Is there any other way I can address my specific requirement?

like image 370
user366312 Avatar asked Jun 27 '18 18:06

user366312


People also ask

Can a generic class definition can only have one type parameter?

A class definition can have more than one type parameter.

Which one is a generic class?

Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees, and so on.

What is generic type constraint?

A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)

What is generic data type?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


3 Answers

There's no such constraint exists in C#. But for value type you can use struct as generic constraint. It will only allow non-nullable value types.

public class A<T> where T : struct
{
    public T property;
}

You can add a runtime type checking in constructor:

public class A<T> where T : struct
{
    public T property;
    public A()
    {
        if(typeof(T) != typeof(int) || typeof(T) != typeof(double))
        {
            throw new InvalidConstraintException("Only int or double is supported");
        }
    }
}
like image 59
vendettamit Avatar answered Nov 18 '22 09:11

vendettamit


You could write your own Wrapper and use a basic interface for them. For example:

public class Integer : IMyNumber
{
    public int Value { get; set; }


    public Integer() { }
    public Integer( int value ) { Value = value; }


    // Custom cast from "int":
    public static implicit operator Integer( Int32 x ) { return new Integer( x ); }

    // Custom cast to "int":
    public static implicit operator Int32( Integer x ) { return x.Value; }


    public override string ToString()
    {
        return string.Format( "Integer({0})", Value );
    }
}


public interface IMyNumber
{
    // nothing needed
}

Then you can write your generic class:

public class A<T> where T : IMyNumber
{
    public T property;
}

And you can use it:

A<Integer> i = new A<Integer>();
i.property.Value = 10;
like image 3
S.Spieker Avatar answered Nov 18 '22 10:11

S.Spieker


You can add private constructor to A<T> class and declare two corresponding classes: A_int and A_double inside it to have possibility to inherit them from A<T> - they become "friendly" to A<T>. But for classes declared outside that scope(Test class) it won't possible as well as for direct creation due to private constructor, which we have to invoke, but can't. So, practically, you will have only two usable variants of A<T> complimented with compile time notifications of not allowed usages:

public class A<T> where T : struct
{
    //constructor surely can have arguments
    private A()
    {
    }

    public T property { get; set; }
    //and other common stuff

    //each class declaration below we can treat like "where" constraint
    public class A_int : A<int> { }
    public class A_double : A<double> { }
}

//compile time error:
//'A<bool>.A()' is inaccessible due to its protected level
public class Test : A<bool>
{
}

Usage:

using static NameSpaceName.A<int>;
//you should not care about <int> - it is only needed for compiler 
//and won't have any influence

var intVar = new A_int();
var doubleVar = new A_double(); 

//compile time error:
//'A<decimal>.A()' is inaccessible due to its protected level
var decimalVar = new A<decimal>(); 
like image 1
Slava Utesinov Avatar answered Nov 18 '22 10:11

Slava Utesinov