2

I was trying understand a extension method to convert string to Enum found here.

public static T ToEnum<T>(this string value, T defaultValue)
{
     if (string.IsNullOrEmpty(value))
     {
        return defaultValue;
     }

     T result;
     return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

Compile Error:

The type 'T' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'Enum.TryParse(string, bool, out TEnum)'

One the Comments says to add where T : struct for the extension method to work.

Method With Constraint:

public static T ToEnum<T>(this string value, T defaultValue) where T : struct
{
    if (string.IsNullOrEmpty(value))
    {
          return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

I read the docs on Enum.TryParse , but I don't understand in docs on why where T : struct is added as constraint for type T ?

Why would above extension wont work without constraint as struct why not other value types ? How to relate struct and Enum type ? or Is it just a syntax to follow ?

Update:

Most of them said any value type can be used , I tried to use where T : int But I get compile time error:

'int' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.

Shaiju T
  • 6,201
  • 20
  • 104
  • 196
  • 1
    This constraint prevents passing types that are certainly not enum types to that method. If it were possible to restrict even more to `where T: Enum` - that would be better, but it's not possible. – Evk Dec 15 '17 at 09:21
  • @HimBromBeere , why only `struct` as constraint ? Why not other value types ? – Shaiju T Dec 15 '17 at 09:22
  • @stom Which other value types are you talking about? – Sir Rufo Dec 15 '17 at 09:24
  • Now I see your point, good question. +1 – MakePeaceGreatAgain Dec 15 '17 at 09:24
  • @Evk , Do You mean `struct` is `Enum` type ? – Shaiju T Dec 15 '17 at 09:25
  • @stom No, an *enum* is a *struct* but never a *class* so the best constraint to use is *struct*, because there is no constraint for *enum* which would fit better – Sir Rufo Dec 15 '17 at 09:26
  • 1
    Maybe you are confused by thinking that struct is only type you define with `public struct SomeType {...}`. That's not so - many built-in types are also "structs" (value types), such as int, long, DateTime, Enum and so on. – Evk Dec 15 '17 at 09:30
  • Possible duplicate of [Create Generic method constraining T to an Enum](https://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum) – Wai Ha Lee Dec 15 '17 at 09:39
  • It is necessary because Enum.TryParse<> has this constraint. So you can't write "T can be anything" generic code when the method demands "You can only call me when T is a value type". Fwiw, ideally T would be constrained to be an enum type. That is however not possible in C#, a tricky problem related to an enum having an underlying type that determines its size. Size matters a great deal to generic code. – Hans Passant Dec 15 '17 at 09:46

2 Answers2

1

The constraint struct does not only include types you declared via struct MyStruct, but also many built-in-types such as int, byte or char. Actually the constraint restricts T to be of any value-type, see this from the C#-specification 9.4.5 Satisfying constraints:

If the constraint is the value type constraint (struct), the type A shall satisfy one of the following

  • A is a struct type or enum type, but not a nullable value type. [Note: System.ValueType and System.Enum are reference types that do not satisfy this constraint. end note]
  • A is a type parameter having the value type constraint (§15.2.5)

Of course it´ll be nice if there were a more restricitve constraint.

As of your edit: your compiler-message is pretty clear in this case. When using a type as constraint, you can use only interfaces or non-sealed classed. Restricting a generic type-parameter to match exactly one single class (or struct) would make a generic quite useless, wouldn´t it? Restricting your generic to int will effectivly turn your method-signature in something like this:

public static int ToEnum(this string value, int defaultValue)

because there´s only one struct matching the constraint.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • `the constraint restricts T to be of any value-type` , Then why I am not able to use `where T : int` ? `int` is a value type right ? – Shaiju T Dec 18 '17 at 11:20
  • `When using a type as constraint, you can use only interfaces or non-sealed classes.` Where does `struct` comes in this category ? – Shaiju T Dec 18 '17 at 11:51
  • 1
    @stom Not at all. It´s about a **type**-constraint. not the **value-type**-constraint (see the part from c#-spec). You can write `where T: struct` which allows **all** value-types, or `where T: MyInterface` where `T` is the type-constraint allowing only instances of the interface. – MakePeaceGreatAgain Dec 18 '17 at 11:54
  • I have final question , I don't have option to use where T : Enum , because compiler doesn't allow. Ok why does compiler allow `where T : struct` ? isn't because it belongs to syntax of `Enum.TryParse` ? – Shaiju T Dec 18 '17 at 12:01
  • `You can write where T: struct which allows all value-types`, Can you prove that statement by creating a extension method that accepts `int` and converts to `Enum`? – Shaiju T Dec 18 '17 at 12:22
  • @stom I can´t see how this would improve the answer in any way. However you can easily try this out on your own. – MakePeaceGreatAgain Dec 19 '17 at 08:19
0

This is done to enable partial error checking at compile time:

  • Reference types are rejected at compile time,
  • Value types that are not enums are rejected at runtime.

It would be better if you could write where T : Enum, but C# compiler prohibits this:

A constraint cannot be special class `System.Enum'

For more information on this error see this Q&A. In particular, see this answer for a short work-around to the problem.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523