Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

C#: switch, is, type, typeof(), GetType()

Discussion in 'Scripting' started by Harter, Nov 28, 2012.

  1. Harter

    Harter

    Joined:
    Feb 28, 2012
    Posts:
    119
    Hi, programmers!

    This one works pretty well:
    Code (csharp):
    1.     if (Collided[Collided.Count - 1] is Human)
    2.         Debug.Log("WORKS!");
    And this one doesn't even want to compile:
    Code (csharp):
    1.     switch (Collided[Collided.Count - 1].GetType())
    2.     {
    3.         case typeof(Human):
    4.             Debug.Log("WORKS!");
    5.             break;
    6.     }
    What am I doing wrong?

    Thanks.
     
    Last edited: Dec 28, 2012
  2. tmills

    tmills

    Joined:
    Sep 20, 2012
    Posts:
    6
    I think you're probably taking the wrong approach to your problem. You're likely trying to implement polymorphism without knowing it. You'd be better off giving human some sort of "HandleCollision" virtual method, and doing something like:

    var human = obj as Human;
    if (human != null) human.HandleCollision();

    Alternatively, you could create an interface with a "HandleCollision" method and anything that should respond to collisions can implement that interface, and the code above would simply be changed to:

    var collider = obj as MyInterface;
    if (collider != null) collider.HandleCollision();
     
  3. Landern

    Landern

    Joined:
    Dec 21, 2008
    Posts:
    354
    You can not use a type as a case in a switch statement. However you could(COULD) use the string version of the type instead and that would work fine.

    Code (csharp):
    1.  
    2.     switch (Collided[Collided.Count - 1].GetType().ToString())
    3.     {
    4.         case "Human":
    5.  
    6.             Debug.Log("WORKS!");
    7.  
    8.             break;
    9.     }
    10. }
    11.  
     
    Last edited: Nov 28, 2012
    SamFernGamer4k likes this.
  4. marcoantap

    marcoantap

    Joined:
    Sep 23, 2012
    Posts:
    215
    I converted some code of mine from Unityscript to C#, and that's one of the downsides. I replaced the type switches with a chain of if..else.

    Please notice that it's not the same to use "obj.GetType() == typeof(obj)" instead of "is". Read this article to understand the differences: http://blogs.msdn.com/b/vancem/archive/2006/10/01/779503.aspx
     
  5. Harter

    Harter

    Joined:
    Feb 28, 2012
    Posts:
    119
    tmills, you're right!

    Thank you, guys. It helped.
     
  6. programmerguy2341

    programmerguy2341

    Joined:
    Nov 9, 2019
    Posts:
    1
    Hey all I know this is an old thread but I wanted to shout out C# 7 added pattern matching which making switching on types much easier. Here is a quick code snippet I took from this blog which explains how to C# switch on types.

    Code (CSharp):
    1. switch (vehicle)
    2. {
    3.     case Car car:
    4.         WriteLine("Car!");
    5.         break;
    6.     case Truck truck:
    7.         WriteLine("Truck!");
    8.         break;
    9.     case null:
    10.         throw new ArgumentNullException();
    11.         break;
    12.     default:
    13.         // Anything other than Car, Truck or null
    14.         throw new UnknownVehicleException();
    15.         break;
    16. }
    Cheers!
     
  7. Dev-4U

    Dev-4U

    Joined:
    Nov 2, 2021
    Posts:
    23
    Though Unity 2019.3 is supposed to support C# 7.3 I cannot switch on types, I tried this:

    Code (CSharp):
    1.             switch (GetType())
    2.             {
    3.                 case MonoBehaviour mb:
    4.                     break;
    5.                 case typeof(MonoBehaviour) mobe:
    6.                     break;
    7.             }
     
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,921
    Because none of your two code examples make sense. GetType returns a System.Type object that describes the type of whatever you call GetType on. However that System.Type object has nothing to do with your specific instance of the type. It's part of the reflection and type system of C#. You try to use the new switch statements to auto-cast a reference which however is not a reference to your instance but just the System.Type object.

    Since you just called
    GetType()
    , you essentially did call
    this.GetType()
    . If you want to check the type of
    this
    you would have to do

    Code (CSharp):
    1. switch (this)
    2. {
    3.     case MonoBehaviour mb:
    4.         break;
    5. }
     
  9. Dev-4U

    Dev-4U

    Joined:
    Nov 2, 2021
    Posts:
    23
    Thanks! That explains why it wouldn't work. Use the object, not the type, even though you want to switch on "Type". Kind of confusing. :)

    That means I have to stick to this because all I have at this point is the System.Type:
    Code (CSharp):
    1.         private static string GetConvertMethodForType(System.Type type)
    2.         {
    3.             switch (type.Name)
    4.             {
    5.                 case nameof(System.Single):
    6.                     return "System.Convert.ToSingle";
    7.                 case nameof(System.Byte):
    8.                     return "System.Convert.ToByte";
    9.                 case nameof(System.Int16):
    10.                     return "System.Convert.ToShort";
    11.                 case nameof(System.Int32):
    12.                     return "System.Convert.ToInt32";
    13.                 case nameof(System.Int64):
    14.                     return "System.Convert.ToInt64";
    15.                 case nameof(System.Boolean):
    16.                     return "System.Convert.ToBoolean";
    17.                 case nameof(System.String):
    18.                     return "System.Convert.ToString";
    19.                 case null:
    20.                 default:
    21.                     return string.Empty;
    22.             }
    23.         }
     
    steril likes this.
  10. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,773
    This is one of those unfixable methods, because either a) it's the only way, or b) if you have a method like this the entire concept behind why this method is needed is already broken. B is much more likely.

    Put another way, trying to improve this function is like trying to improve the SmackSelfInFace() method. It might be the best way to smack yourself in the face but why would you do that?

    What's calling GetConvertMethodForType?
     
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,921
    While string comparisons may be necessary or the only way in some cases, note that a switch case in the end is translated to some other form of branching / analysis wrapping code. For simple ordinal types with no gaps, a switch case usually creates a jump table where the input is directly used to jump to a certain code fragment. When the cases do not show good patterns that can be exploited by the compiler / Jitter, if you only have a few cases it would resort to an if-else chain. If there are more cases it almost always creates a look up dictionary.

    In this case I would also recommend to manually use a dictionary, especially when the method is used a lot. In a dictionary you can use System.Type objects as keys since they are also just object references.

    Code (CSharp):
    1.     public static Dictionary<System.Type, string> m_ConvertMethods = new Dictionary<System.Type, string>(){
    2.         {typeof(System.Single), "System.Convert.ToSingle"},
    3.         {typeof(System.Byte), "System.Convert.ToByte"},
    4.         {typeof(System.Int16), "System.Convert.ToShort"},
    5.         {typeof(System.Int32), "System.Convert.ToInt32"},
    6.         {typeof(System.Int64), "System.Convert.ToInt64"},
    7.         {typeof(System.Boolean), "System.Convert.ToBoolean"},
    8.         {typeof(System.String), "System.Convert.ToString"},
    9.     };
    10.  
    11.     private static string GetConvertMethodForType(System.Type type)
    12.     {
    13.         if (m_ConvertMethods.TryGetValue(type, out string methodName))
    14.             return methodName;
    15.          return null;
    16.     }
    17.  
     
    Dev-4U likes this.
  12. Dev-4U

    Dev-4U

    Joined:
    Nov 2, 2021
    Posts:
    23
    Thanks for converting this into a dictionary! That makes more sense. :)
    Also new to me: declaring out parameters within the parameter list. What a blessing! This also works nicely with "var" btw!

    I use this formerly-switch code to generate a class whose constructor only receives a List<object> imported from a spreadsheet file, hence I have to look up the proper Convert.ToSomething method since casting won't do (casting != unboxing). It's one of those unconventional cases where you generate C# scripts that import data from another text file and need to figure out how to convert that to int, float, etc.

    This is used in the Spreadsheet Database 4U tool (SSDB) I'm developing.
     
    Last edited: Jan 14, 2022
  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,921
    Yes, that's actually a relatively new feature in C# (can't remember the C# version, I think 7?)

    Not sure what you actually want to do, but you can change / convert an object to another type by using System.Convert.ChangeType. The return value is still of type Object but the actual type is converted. Maybe that's what you actually want to do? You can do almost anything with reflection in a dynamic way. Though some things get exponentially more complex.
     
  14. Dev-4U

    Dev-4U

    Joined:
    Nov 2, 2021
    Posts:
    23
    Correct, C# 7 (actually 7.1, seems like there never was a public 7.0). That was August 2017 and a year later I stopped programming C# until now. I think first Unity version to support 7 was 2019.something. 2019.4 is what I'm working with.

    I do need to use the individual ConvertTo since the object has to be unboxed and is then assigned to a field with the specified type. As in:

    int i = Convert.ToInt32(obj);