A generic class which can take only certain types

Multi tool use
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?





You can't - how would you do anything with property within A?
– Lee
Jun 27 at 18:32


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.

X pjrKJB6u9zQh4XwE9M97jNQYjyysOOSmzUEZPm271v6kpC2NuNguHu5Hlj8gfcXiCJP
t2CikwZx

Popular posts from this blog

Rothschild family

Cinema of Italy