How to restrict T to value types using a constraint?

16,072

Solution 1

Unfortunately, it is not possible to specify generic type constraints that only allow specific value types. More to the point, it wouldn't make much sense even if it was allowed.

You're allowed to specify a class as a generic constraint but this is because you can inherit from classes, the constraint thus sets the minimum threshold of what types you're allowed to use.

If this was allowed for value types, where you cannot inherit from those types, you would effectively limit yourself to only that type.

Thus you cannot do this, but you have a few alternatives:

  • You can declare it without the constraint, and handle the problem at runtime. I would not recommend this way
  • You can declare overloads that take the specific types you're interested in.

    Since you only have two such types this is what I would recommend doing.

Here are the overloads you would declare:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
    // Do stuff here
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
    // Do stuff here
}

Now, additionally, if your handling of those values doesn't really rely on the numeric quality of those types, you just want to limit which types you can handle, then you can always declare your original method as well, privately, and call this method from your overloads. This would still limit your code to only allowing int or decimal, publicly, but your implementation would still be generic. Without knowing exactly what "Do stuff here" entails it is impossible to tell if this is a viable option or not but here is the code anyway:

public static Chart PopulateInto<T>(List<T> yAxis, List<int> xAxis)
{
    return PopulateInto<T, int>(yAxis, xAxis);
}

public static Chart PopulateInto<T>(List<T> yAxis, List<decimal> xAxis)
{
    return PopulateInto<T, decimal>(yAxis, xAxis);
}

private static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : struct
{
    // Do stuff here
}

Solution 2

It's not possible to constrain a generic parameter to a specific value type.

You can however force it to be a value type or struct by adding where N : struct, but that's all.

Solution 3

There is no way to do this with a constraint. Another approach though, assuming that PopulateInto can work with a generic N, is to make the core algorihtm generic and private and offer 2 public overloads which take an int and decimal respectively. This will create a similar effect

public static Chart PopulateInto<T>(
  List<T> yAxis, 
  List<decimal> xAxis) {

  return PopulateIntoCore(yAxis, xAxis);
}

public static Chart PopulateInto<T>(
  List<T> yAxis, 
  List<int> xAxis) {

  return PopulateIntoCore(yAxis, xAxis);
}

private static Chart PopulateIntoCore<T, N>(
  List<T> yAxis, 
  List<N> xAxis) where N : struct {
  ...
}

Solution 4

As Pieter said, you cannot use a compile-time check to to this. However, you can do the following at runtime:

if(!(typeof(N).equals(typeof(int32))) && !(typeof(N).equals(typeof(decimal))))
  // do something

Solution 5

To answer the question in the Title but not the body of the question.

To cover all types commonly meant by Value Types (which includes Nullable Value Types, and also string even though it's technically a Reference type), you need 3 overloads:

public void Foo<T>(T arg) where T : struct
public void Foo<T?>(T? arg) where T : struct
public void Foo<string>(string arg)

From the MSDN Docs on generic constraints:

where T : struct The type argument must be a non-nullable value type.

Share:
16,072

Related videos on Youtube

Prisoner ZERO
Author by

Prisoner ZERO

Updated on November 21, 2020

Comments

  • Prisoner ZERO
    Prisoner ZERO over 3 years

    I want to restrict the possible types N can take-on using a constraint. I wish to restrict N to be either a int or a decimal.

    public static Chart PopulateInto<T, N>(List<T> yAxis, List<N> xAxis) where N : int, decimal
    {
        // Do stuff here
    }
    

    Any help appreciated...

    • John Bledsoe
      John Bledsoe over 13 years
      If those are the only two types you care about I think you could just overload the method. Sure it's 4 overloads but that's not too bad.
  • Jeff Mercado
    Jeff Mercado over 13 years
    An alternative way to handle it is to have specific overloads for int or decimal lists.
  • JaredPar
    JaredPar over 13 years
    @Mark, yeah, that would be the crux of my answer ;) Fixed
  • Lasse V. Karlsen
    Lasse V. Karlsen over 13 years
    And my generics are off, that's what I get for writing answers on Friday night, let me correct that.
  • user1703401
    user1703401 over 13 years
    Yeah, why not. Throw an InvalidOperationException.
  • Lasse V. Karlsen
    Lasse V. Karlsen almost 6 years
    Thank you for your constructive criticism. Unfortunately the question in the title is misleading, because if we limit ourselves to that then the answer is easy: where N : struct. However, the real question only has one real, definitive, answer, and that is "That is not possible with the current allowed C# syntax". I have expanded my answer to cover all the options though, hope it wastes less of peoples time going forward.