Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

is there a shorter method for cast and call

Discussion in 'Scripting' started by craig4android, Oct 8, 2019.

  1. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
    Code (CSharp):
    1. var a;
    2. ...
    3. ...
    4. if(a is t1){
    5. t1 t=a as t1;
    6. t.callSomething();
    7. }
    this code is so bulky, so is there a shorter version to call "callSomething" if a is t1
     
  2. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,202
    You can marginally shorten that by removing the "is", since the "as" will simply return null if the conversion is unsafe:
    Code (CSharp):
    1. var a;
    2. ...
    3. ...
    4. t1 t = (a as t1);
    5. if (t != null) t.callSomething();
    One could also wrap all of that in a helper function.

    If you need to do this on a regular basis, though, it might be time to rethink how you're using polymorphism.

    Sometimes it's appropriate to move that function into the base class with a default implementation that does nothing. For instance, maybe you need a "perform all nightly maintenance" function, but some subtypes don't have any maintenance to perform; it might make more sense to implement the function on all types (with some of them doing nothing when called) rather than require the caller to explicitly check the subtype every night before starting maintenance.

    Or maybe whatever thing the caller is doing should itself be turned into a method on the object, so that it will implicitly have all necessary information about the object while doing that thing.
     
  3. Mordus

    Mordus

    Joined:
    Jun 18, 2015
    Posts:
    165
    You also have the ?. operator.
    Code (CSharp):
    1. (variable as Type)?.Function();
     
  4. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,202
    You generally shouldn't use ?. on any Unity type (including anything derived from MonoBehaviour) because of the weird pretend-null stuff Unity does.

    If the type in question is unrelated to Unity and your project is set to use a sufficiently-recent .NET version, then sure.
     
  5. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
    ty that was what I was looking for
     
    Last edited: Oct 8, 2019
  6. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,768
    With C# 7 you can do this:
    Code (CSharp):
    1. if(a is t1 t)
    2. {
    3.     t.callSomething();
    4. }
     
    palex-nx likes this.
  7. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,393
    Heed Antistone's warning, or you'll pay the price (usually half your hair per week).

    Also - and I know that it's a pet peeve of mine - I firmly believe that what you call 'bulky' code simply is clear code. You are doing something that is non-Standard, so the code should carefully explain what you are doing. Casting a pointer should always be self-elaborating in code. The compiler will NOT genereate more code if you use additional statements for clarity, the optimizer will take care of that. So if you ever plan to revisit your code later, be greatful if your casting is as Elaborate as possible.

    Yes, IMHO. But that's all I have :)
     
  8. Ardenian

    Ardenian

    Joined:
    Dec 7, 2016
    Posts:
    277
    C Sharp supports Extension Methods (C# Programming Guide). You could write an extension method for your type of a and as a result then simply write:
    a.CallSomething()

    with CallSomething() being an extension method on your type a, handling the conversion to type of t1. If this is a good approach is debatable, but it would be one of the shortest solutions.

    Pseudo-Code for the extension method could look like this:
    Code (CSharp):
    1. public static class MyExtensions
    2.     {
    3.         public static void CallSomething(this TypeA a)
    4.         {
    5.             if(a is TypeOne t) t.CallSomething();
    6.         }
    7.     }  
     
    FernandoHC likes this.
  9. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
    hmm I read it, but I'm not quite sure if I understand it right. Unity overrides the "==" of GameObjects, so a destroyed Object appears to be null even if it isn't really null. But in this particular case it shouldn't be a problem since I'm not going to call the method anyways if the object is supposed to be destroyed. Or am I missing something?
     
  10. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,393
    eses likes this.
  11. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
  12. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    1,559
    I may be just ignorant about something here, but for which usecase is this required?

    I've personally never really used var and never missed it. To me it's mostly a "lazy prototyping tool". Thus i dont think that there is any situation in which it is required, which implies you could prevent the "bulky code" by simply not using var and instead doing it "properly" in the first place. But i'm curious if i'm missing something here.
     
  13. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    7,965
    It's good for particularly lengthy data types:
    Code (csharp):
    1. Dictionary<string, List<Vector3>> someLongList = new Dictionary<string, List<Vector3>>();
    2. //add stuff
    3. foreach (KeyValuePair<string, List<Vector3>> kvp in someLongList) {
    Is a little absurd both to type and to read, let alone refactor. But:
    Code (csharp):
    1. var someLongList = new Dictionary<string, List<Vector3>>();
    2. //add stuff
    3. foreach (var kvp in someLongList) {
     
  14. csofranz

    csofranz

    Joined:
    Apr 29, 2017
    Posts:
    1,393
    No no no no no no no no no noooooooo!!
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,345
    @StarManta noted a place where it is convenient.

    But I can think of a place it's required. And that is when you are dealing with anonymous types. You don't know the type (it's generated at runtime), so you can't declare the type when declaring the variable, therefore you must use var.

    Of course anonymous types aren't required either, but if you use them, then you're required to use var if you expect to have a variable that stores them (without downcasting to object, or using dynamic).

    And personally I like var for the very reason StarManta suggested. It can make code more readable. It could be argued there are use cases that are less readable, and I would avoid those specific cases of using var. That's basically my case by case taste of it... if it makes my line of code more confusing, I won't do it.

    This of course could be said of explicit typing as well... it can be more readable in some cases, and less in others. Saying int i = 0, vs var i = 0, var is more confusing. But saying StarManta's scenario, the explicit type is more confusing. So the fact var can cause less readability in edge cases isn't an argument against it, since the same can be said of explicit typing.

    I'd compare it to a 'contraction' in English. It's useful for shortening a phrase, at the expense of sometimes being confusing if it becomes a homophone to another word in the sentence. So in most cases it's a great shorthand, and you only avoid it in those edge cases. But if you don't want to use it, well have at it... say all the 'can not' 'are not' and 'did not's that you want.
     
    Last edited: Oct 8, 2019
    StarManta likes this.
  16. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    1,559
    I see the convenience in the foreach loop, even tho personally i would prefer the longer type declaration. I guess this is rather subjective anyways, since it's optional to use.

    Still optional overall, as you said, but sure that's a scenario in which they definitely are useful.

    Thanks for the clarifications at both of you. I guess i personally just prefer knowing all the types directly, and making heavy use of var can result in longer lookup times for what type exactly you are looking at. It may just be that i dont like var since i used to work with somebody who abused the living sh*t out of it (other language tho) and his code was really confusing to read and work with. However, for some infrequent cases i guess it is a convenience to improve readability :)
     
  17. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,345
    Mind you that var works very different in C# than it may in other languages.

    I'm not sure which language you're referring to, but if it's per chance 'javascript', they behave way different. 'var' in javascript behaves more like 'dynamic' in C#.

    'var' in C# requires that the compiler should be able to infer the data type from context.

    For example:
    Code (csharp):
    1. var dict = new Dictionary<string, int>();
    We can easily infer what dict is because the variable is being declared and set to a Dictionary<string, int>, clearly it's a Dictionary<string, int>.

    Where as:
    Code (csharp):
    1. var obj = null;
    This is invalid, we can't infer what 'obj' is going to be since so many types could potentially be set to null. The compiler, nor ourselves reading it, can infer what the type is. And therefore this wont' compile.

    Effectively, when a 'var' variable is declared, something immediately following it (usually an assignment) will infer its type. Otherwise compiler complains.

    The one place where I can see this getting confusing is when accessing members of another object, like enumerables, or properties:

    Code (csharp):
    1. foreach(var obj in someList)
    Code (csharp):
    1. var obj = someReference.someProperty;
    Here we must know what type someList is, or someProperty is, to infer what obj is. The compiler can infer it easily, but readability wise you may not. And here is where your naming conventions of variables play a very important role... or you just avoid the 'var' in the cases where the naming convention sucks (say you can't control it, like the property of a class coming from some 3rd party library).

    For example, lets consider these:

    Code (csharp):
    1. var go = collider.gameObject;
    Code (csharp):
    1. byte[] byteData = someObj.data;
    The first one is obvious that gameObject is of type GameObject, so the use of 'var is not confusing.

    But the 2nd, data could be anything, so I've explicitly typed it to what it is, and gave it a better name for my local scope.
     
    Last edited: Oct 8, 2019
  18. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
    for example:

    Code (CSharp):
    1. public void doSomething(object o){
    2.  
    3. if(o is type1){
    4. //do something
    5. }
    6. else if(o is type2){
    7. //do something else
    8. }
    9.  
    10. }
     
  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,749
    Absolutely not required. Besides the fact that var does not change anything about the strongly typed system, it can be very beneficial for overall readability and aesthetics, but that's subjective.

    Perhaps you'll start to like it once you've used it. Some years ago I thought the same, I had the same mindset and never thought this would ever be useful for me, why would I want to omit type information... silly language devs *rolling eyes*... until I finally started to use it.

    Soon it turned out it made me choose better identifiers.
    Code looked much cleaner, no unnecessarily long types in front of the actual identifiers.
    And then, there are multiple lines of variable declarations... No difference in type length, lines start with the same textual pattern, incredibly satisfying, easier to read. Still subjective, but I don't wanna miss it.
     
    Yoreki and lordofduct like this.
  20. Antistone

    Antistone

    Joined:
    Feb 22, 2014
    Posts:
    2,202
    This would only make sense if you don't have (or aren't allowed to modify) the source code for the actual classes in question. If you can modify the class, then adding the function to the base type makes more sense than creating an extension method that checks for the subtype, because then you can take advantage of built-in polymorphism features instead of using the relatively-expensive conditional casting, plus it'll be easier to extend for any new subtypes you create.

    Note that while the OP happened to use "var" in the example, the original question has absolutely nothing to do with "var". The example could just as easily have been:
    Code (CSharp):
    1. BaseClass a;
    2. ...
    3. ...
    4. if (a is SubClass)
    5. {
    6.     SubClass b = a as SubClass;
    7.     b.callSomething();
    8. }
     
    Yoreki likes this.
  21. craig4android

    craig4android

    Joined:
    May 8, 2019
    Posts:
    113
    O.k. just tested it the difference between "==null" and ".?"

    "==null" will return true even if the reference still exists but GameObject.destroy was called on the GameObject

    while ".?" does check for reference null
     
  22. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,345
    Yes.

    Unity in their brilliance early on made an override for the == operator on UnityEngine.Object that equates destroyed objects to null.

    Problem is:

    1) It only works with the == operator and != operator. None of the null coalescing operators and the sort work with it. Super freaking annoying.

    2) Since it's a static operator defined on UnityEngine.Object, that means if you have your object cast to anything that doesn't inherit from UnityEngine.Object (like System.Object, or an interface it happens to implement) then the == and != operator fails to behave the same.

    A few years back we all complained massively about it, and Unity considered changing it. But after some internal deliberation they decided not to as it would break a lot of backwards compatibility and what not.

    So instead we all just have to live with this weird little piece of behaviour that easily hides syntactically in plane sight. So annoying.

    Personally I deal with it by not using the null coalescing operator that much, and by creating my own 'IsNullOrDestroyed' method that takes in System.Object and takes into consideration if the type 'is' a UnityEngine.Object.
     
    Suddoha and LiterallyJeff like this.
unityunity