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?
A class definition can have more than one type parameter.
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.
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.)
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.
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");
}
}
}
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;
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>();
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