2

I'm trying to do this:

const string intType = typeof(int).ToString();
switch (typeof(MyT).ToString())
{
    case intType:
    {
        return "int";
        break;
    }
    ...
}

But compiler says:

error CS0133: The expression being assigned to 'intType' must be constant

As I know, typeof operator works at compile-time. So, what's wrong?

Esteban Verbel
  • 738
  • 2
  • 20
  • 39
user1234567
  • 3,991
  • 3
  • 19
  • 25
  • 3
    Even `typeof(int)` will not return a const value. Methods do not guarantee a const return value. – Steve Jan 05 '17 at 16:41
  • You might want to look at using `readonly` instead of `const`, and maybe look at using `Type` instead of `string` as well. – Evil Dog Pie Jan 05 '17 at 16:48
  • @DarrenYoung, thanks. Yes, I've read now about `const` in C# (https://msdn.microsoft.com/ru-ru/library/58918ffs.aspx) & it's much more narrow concept, than in C++ (where I don't really care about "_method returns not-const_" - mostly "_how do I **receive** its output_" determines a behavior). – user1234567 Jan 08 '17 at 07:20
  • @MikeofSST, `Type` - yes, but `readonly` in C# (iinm) could be applied only to fields (& not to locals) – user1234567 Jan 08 '17 at 07:28

4 Answers4

17

As I know, typeof operator works at compile-time.

You don't know that because knowledge has to be true. Where did you get the idea that typeof is executed at compile time? It produces a non-constant object. And then there is no guarantee that ToString doesn't produce a different string every time it runs, so it cannot be treated as a constant either.

So, what's wrong?

You're reasoning from a false belief.

The C# specification clearly describes the conditions that must be met for an expression to be a compile-time constant. Those conditions include the expression not containing any typeof operator or method call.

But there are far bigger problems here. I assume that MyT is a generic type parameter, which means you are attempting to switch on the value of a generic type parameter. That is almost always the wrong thing to do.

What are you really trying to do? What problem are you really trying to solve? Because this code you've shown so far indicates that you're going down an unproductive path to solve whatever the real problem is.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    @Eric, sorry for my inaccuracy in terms and thank you for answer. I'm new to C#. And my picture about `typeof` (what I've called "know") is relied on superficial review of some answers to relative questions. And it looks like `typeof` gives compile-time type in contrast to `GetType` (which gives run-time type). So, thought, there are no reasons to defer to run-time retrieving of compile-time type. – user1234567 Jan 05 '17 at 17:05
  • 5
    @user1234567: Ah, I see the source of your confusion now; thanks for clarifying. Yes, `GetType` returns the type *known to the runtime* of the object, and `typeof` takes as its operand a *known to the compiler* type. But that does not mean that the operation is *executed* at compile time, which is what is necessary for an expression to be a constant. The `typeof` expression produces an object of type `Type`, which is a run-time object. – Eric Lippert Jan 05 '17 at 17:07
  • @Dan, I've answered your question in comment to the lukegv's answer – user1234567 Jan 05 '17 at 17:24
  • 1
    Thank you 1 more time, @EricLippert, but I still confused, why does the compiler defer the `typeof(..)` expression calculation to run time? Is this because of concrete type could depend on system, where the program is executed? – user1234567 Jan 08 '17 at 09:01
  • @user1234567 I guess you can find the answer here https://stackoverflow.com/questions/32212815/why-cant-a-type-be-used-as-a-constant-value – duesterdust Jan 08 '21 at 14:28
3

I think, it is quite obvious, what he wants to achieve:

He wants to check for type equality in a switch-case instead of via if-elseif. And to be honest, why not? But how can he achieve this?

  1. First option: Wait for C# 7.0. Yeah, shit like this is possible in the future!

  2. Second option: Use strings. But the case strings need to be constant. So what about the wonderful nameof?

I just tried this "beauty" and it works, so maybe this solves your problem:

switch (typeof(Int32).Name)
{
    case nameof(Int32):
         Console.WriteLine("It's an Int32!");
         break;
    case nameof(Double):
         Console.WriteLine("It's a Double");
         break;
 }
Lukas Körfer
  • 13,515
  • 7
  • 46
  • 62
  • 1
    This is a nice use of `nameof`, but I still think there is a larger problem here: if you're doing a type switch *at all*, something is probably wrong with the design. There is almost always a better way to solve the problem than switching on a type. – Eric Lippert Jan 05 '17 at 17:02
  • 4
    Also, I'll note that your solution assumes that types are not qualified with their namespaces. You could not for instance have `case nameof(Foo.Bar): ... case nameof(Blah.Bar):` because both would be `"Bar"`. – Eric Lippert Jan 05 '17 at 17:04
  • Yeah, of course. I know, this is just a workaround and you should always check if you need __real__ type comparison. Luckily, with C# 7.0, there will be no need for this workaround. – Lukas Körfer Jan 05 '17 at 17:07
  • 3
    If you want to switch on primitive types you should use `Type.GetTypeCode()`. That'll return an enum value that you can switch over. That would be a lot less fragile than using strings. – Kyle Jan 05 '17 at 17:10
  • @Kyle: Correct, for primitive types only, this would be the better option. – Lukas Körfer Jan 05 '17 at 17:13
  • Actually, I build a visual-scripting node-system allowing to compose user structs in it from base types and other already composed structs. I have something like generic class for nodes. And I just try to feed the NameOfNode property with nice short base types names (like "int" instead of "System.Int32"). PS: Thank you, @lukegv for interesting advise. – user1234567 Jan 05 '17 at 17:18
  • typeof in C# not being constant is really annoying. Even in C# 8, the following code is still not possible `object obj = type switch { typeof(MyClass) => new MyClass()}`. I know the specification says it is not a constant. But can't Microsoft just add a constant_typeof? What's the difficulty? – Xiaoguo Ge Jul 22 '20 at 13:25
2

The only way to compare type MyT with known types is by checking their Type objects for equality. This can be done as follows:

if (typeof(MyT) == typeof(int)) return "int";
if (typeof(MyT) == typeof(decimal)) return "decimal";
// etc...

You cannot use this approach in a switch because (at the moment) a switch requires that the item being checked is of a simple type:

switch (typeof(T)) // Compile error: "switch expression or case label must be a bool,
                   // char, string, integral, enum, or corresponding nullable type"
{
    case typeof(int): return "int";
    case typeof(decimal): return "decimal";
    // ...
}

Also, as others already said, checking types in this way almost always means that your approach can be improved by applying different object oriented principles.

E.g. instead of MyMethod<MyT>(MyT item) with type checks for MyT, consider making MyMethod(int item), MyMethod(decimal item) etc.

Peter B
  • 22,460
  • 5
  • 32
  • 69
-1

If you are just trying to get a string describing the type of object, you just need to call .GetType() instead.

For example, the following is a small function that will return the string name of the object type.

 static string GetTypeString(object obj)
 {
      return obj.GetType().FullName;
 }

This will return to the full path to the object. In int's case, it will return System.Int32. If you only want the Int32 part, use GetType().Name instead.

Also, you don't need to have a break; in a switch if you have a return;

If you have specific code that needs to be run for some types, or a specific string you want to return, you can use a string on the values returned by the above. For example:

   static string GetSimpleType(object obj)
    {
        var stringRepresentation = GetTypeString(obj);
        switch (stringRepresentation)
        {
            case "System.Int64":
            case "System.Int32":
                return "int";

            default:
                return stringRepresentation;
        }
    }

default is a catch all in switch statements for everything that does not have a case. Think of it like an else.

In the above example, we return the same value for int, Int32, and Int64. Case labels can fall through to other case labels if they are empty.

You can find all the values you need to write a switch for by running a simple script, and hard code the string values since they will always be the same for the same types. If the string is different, then the type is different.

Finally, if you are comparing types, if and if else works better:

        static string GetSimpleType(object obj)
        {
           if (obj.GetType() == typeof(int))
           {
               return "int"; 
           }

            return obj.GetType().ToString();
        }