A generic class which can take only certain types

Multi tool use
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.
int
double
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?
property
A
6 Answers
6
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.
struct
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");
}
}
}
can we use explicit constructors to achieve this?
– anonymous
Jun 27 at 18:44
You can put a check in constructor for typeof(T) but that will be runtime check not compile time.
– vendettamit
Jun 27 at 18:48
Using runtimechecks for generics feels wrong to me. Not downvoting, only stating my oppinion here.
– CSharpie
Jul 3 at 7:53
I would put the check in a static constructor instead,
static A() { if … }
.– Jeppe Stig Nielsen
Jul 3 at 18:37
static A() { if … }
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;
The
Integer
type could also be a struct
. In that case I would remove the set;
accessor from the property (you can still assign to its underlying readonly
field in the constructor).– Jeppe Stig Nielsen
Jul 3 at 18:40
Integer
struct
set;
readonly
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:
private
A<T>
A_int
A_double
A<T>
A<T>
Test
private
A<T>
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 want to make sure no-one outside class
A<>
inherits from A<>
, just make sure all non-static constructors are private. You have not written any constructors above, so you get an automatic "default" constructor that is protected
. To avoid that, just add A() { }
inside the class, a private instance constructor.– Jeppe Stig Nielsen
Jul 3 at 18:45
A<>
A<>
protected
A() { }
You can acchive this by using a workarround with static methods and making the constructor internal.
However this only works if A<> is in a different Library. If everything is in the same library, this solution wont work.
public class Program
{
public static void Main()
{
var a = A.CreateDecimal();
a.property = 7;
}
}
public class A<T>
{
public T property;
internal A()
{
}
}
public static class A
{
public static A<decimal> CreateDecimal() => new A<decimal>();
public static A<int> CreateInt() => new A<int>();
}
To my understanding of the C# generics, you have several "modes" in which you can setup your generics:
where T : class
where T : BaseClass
where T : ISomeInterface
To my knowledge there is no way of limiting to which structs or primitives you can use. Probably your best bet would be to create separate implementations using the primitive types you want to use. Alternatively you could create a constructor that checks if the passed in type matches the allowed type and if not throw an exception.
public class MyGenericClass<T>
{
public MyGenericClass()
{
if(typeof(T) != typeof(int)) throw new Exception("You have passed an invalid type");
}
}
Hope this helps.
typeof(T) is int
is illegal. A type object of type System.Type
can never be an int
(inheritance object
-> System.ValueType
-> int
, and the latter is sealed because it is a value type). You can ask if typeof(T) != typeof(int)
instead.– Jeppe Stig Nielsen
Jul 3 at 18:52
typeof(T) is int
System.Type
int
object
System.ValueType
int
typeof(T) != typeof(int)
correct, I was typing this in the browser so I lacked the corrective power of VS. I'll update the code above to reflect that
– Toni Kostelac
Jul 4 at 8:13
If your only goal is to prevent A from being used with anything other than an int
or deouble
, this will work. The XML comment can at least tell potential developers they are limited.
int
deouble
/// <summary>
/// T can only be an int or double or will throw an exception on construction.
/// </summary>
/// <typeparam name="T">Must be int or double.</typeparam>
public class A<T>
{
public A()
{
if (!(property is int || property is double))
throw new Exception("A can only work with int and double");
}
public T property { get; set; }
}
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
You can't - how would you do anything with
property
withinA
?– Lee
Jun 27 at 18:32