97

I have the following method with generic type:

T GetValue<T>();

I would like to limit T to primitive types such as int, string, float but not class type. I know I can define generic for class type like this:

C GetObject<C>() where C: class;

I am not sure if it is possible for primitive types and how if so.

Jason Down
  • 21,731
  • 12
  • 83
  • 117
David.Chu.ca
  • 37,408
  • 63
  • 148
  • 190
  • I think that by "primitive types" @David.Chu.ca means unmanaged types, i.e. `int`, `float` etc. not `Int32`, `Int64`, `Single` etc.. Although `class` denotes a ref type, "but not class type" is suggesting the distinction between unmanaged and managed primitives. – mireazma Sep 18 '20 at 13:55
  • People run into this often. Its worth noting that many of the upvoted answers suggest adding constraints that describe Interface behaviors on T. Heed those answers, because they are good guidance. In 90% of the cases, when you think you need T to be constrained to primitive, what you might really be running into is the desire to perform certain operations on T. For instance, some operations like comparing T==T have non-obvious solutions. T==T is performed with this: EqualityComparer.Default.Equals(a, b). Even though that line is hideous, it compiles to very fast code. – JamesHoux Feb 23 '23 at 05:14

7 Answers7

57

You can use this to limit it to value types:

where C: struct

You also mention string. Unfortunately, strings won't be allowed as they are not value types.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
BFree
  • 102,548
  • 21
  • 159
  • 201
  • 6
    but not for string which is nullable – David.Chu.ca Apr 30 '09 at 03:47
  • 6
    And of course it lets you pass any user-defined struct type, not just primitive types. I'm not sure there's a way, really, other than defining overloads for all the built-in primitive types. – Matt Hamilton Apr 30 '09 at 03:49
  • 2
    The question was for primitives, not value types. As you mention in your answer the string primitive is not a value type, so it doesn't work in that regard -- also it will allow you to pass random structs (as @MattHamilton points out) which are not primitives. – BrainSlugs83 Jul 11 '18 at 19:37
  • 1
    With this if I have a method that does `n1 + n2` where n1 and n2 both are of type `C`, it is throwing me the error: `Operator '+' cannot be applied to operands of type 'C' and 'C'` – Sisir Jun 18 '19 at 05:47
31

Actually this does the job to certain extend:

public T Object<T>() where T :
   struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>

To limit to numeric types you can get some useful hints of the following samples defined for the ValueType class

Deko
  • 439
  • 4
  • 4
  • 1
    **Note:** in case you are using this to feed ID field to database, same as me, Guid struct does not support `IConvertible` interface, and you may want to remove it. – Hassan Faghihi Dec 13 '20 at 10:08
18

Here's what you're looking for:

T GetObject<T>() where T : struct;
Dan
  • 1,805
  • 2
  • 18
  • 21
Joshua Belden
  • 10,273
  • 8
  • 40
  • 56
11

There is no generic constraint that matches that set of things cleanly. What is it that you actually want to do? For example, you can hack around it with runtime checks, such as a static ctor (for generic types - not so easy for generic methods)...

However; most times I see this, it is because people want one of:

  • to be able to check items for equality: in which case use EqualityComparer<T>.Default
  • to be able to compare/sort items: in which case use Comparer<T>.Default
  • to be able to perform arithmetic: in which case use MiscUtil's support for generic operators
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
9

What are you actually trying to do in the method? It could be that you actually need C to implement IComparable, or someother interface. In which case you want something like

T GetObject<T> where T: IComparable
David McEwing
  • 3,320
  • 18
  • 16
4

I'm under the same need, I want to create a method that should retrieve a List where T should be a primitive type like int, double, decimal, etc...

Based on this Microsoft documentation: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types

Looks like the right approach is to use

where T : unmanaged

quoting:

A type is an unmanaged type if it's any of the following types:

sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, or bool Any enum type Any pointer type Any user-defined struct type that contains fields of unmanaged types only and, in C# 7.3 and earlier, is not a constructed type (a type that includes at least one type argument)

Also important quote:

Beginning with C# 7.3, you can use the unmanaged constraint to specify that a type parameter is a non-pointer, non-nullable unmanaged type.

Beginning with C# 8.0, a constructed struct type that contains fields of unmanaged types only is also unmanaged...

Yogurtu
  • 2,656
  • 3
  • 23
  • 23
  • 1
    To get around C# 8.0's expansion in seeing structs as `unmanaged`, you can probably still restrict via the `unmanaged, IComparable, IFormattable, IConvertible, IComparable, IEquatable` yadda as before. I wish primitives would actually implement something allowing to uniquely recognize them (like `IPrimitive` that cannot be implemented by others)... – Ray Jul 25 '22 at 12:22
  • 1
    `unmanaged` does not contain the `string` type, just need add an overload for `string`. – Mr. Squirrel.Downy Jun 09 '23 at 01:01
0

If you need types for which you can use languages features reserved for managed types such as the sizeof operator, use "unmanaged".

where C: unmanaged