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
  2. Enter the 2020.2 Beta Sweepstakes for a chance to win an Oculus Quest 2.
    Dismiss Notice

Unity C# 8 support

Discussion in 'Experimental Scripting Previews' started by Jes28, Apr 18, 2019.

  1. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    1,375
    I've always wondered:

    With default interface implementations being a thing, is class-inheritance now just redundant? The only thing interfaces are missing now is the ability to declare non-static fields, and who knows, maybe that's the logical next step in this direction & that will be added in the future.

    It's already been said & done in the past about how/why a class cannot inherit from multiple other classes, but now it seems we've reached the ability to do this anyway with interfaces instead.
     
  2. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    8
    Unfortunately default interface implementation will not help you.
    If struct doesn't reimplement default interface method than it will be boxed. even in generic method.
    Calling default implementation requires virtual call so structs need to be boxed to get method table for virtual call.
    Structs should always implement all interface methods (even default one) if you need performance
     
    Last edited: Sep 17, 2020
  3. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    471
    Seriously? Was hoping that default implementation would just "copy" the implementation to the places where it is not implemented... Well, good to know
     
  4. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    8
    interfaces has inheritance by itself.
    Interface could override default implementations in base interface, so "just copy" will not work:(
    also you couldn't directly call default implementation, you should cast to interface first.
    Code (CSharp):
    1. interface Interface
    2. {
    3.     void DoSomething() => Debug.Log("Default");
    4. }
    5.  
    6. struct Struct : Interface
    7. {}
    8.  
    9. ....
    10. var s = new Struct () ;
    11. //s. DoSomething (); <- will not work
    12. ((Interface) s). DoSomething () // <- works
    Similar happen behind the scene in generic methods.
    also same for classes :)
     
  5. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    471
    According to here: https://stackoverflow.com/questions...ethod-from-implementing-struct-without-boxing

    JIT can avoid the boxing for this use case, so (hopefully) Unity can think of a way to Burst to optimize it as well.

    In the end it just work as an implicit implementation from what I can tell, if that is the case it will still be useful for my use case.
     
    VolodymyrBS likes this.
  6. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    8
    thanks for link.
    yeah probably I miss some cases.
    looks like JIT could avoid boxing if default implementation is "pure" (doesn't call other methods from same interface or something like that).
    Probably more optimization could be done in future.

    But still a lot of similar problems could be solved with extension methods. And it will not rely on JIT/burst optimization

    P.S. my small experiments with this
    https://sharplab.io/#v2:EYLgtghgzgL...B2TOThlRCF+KkhNqxK1pIpVOk0tlFFZ7NhKp5J0tkJaQA
     
    Last edited: Sep 18, 2020
    jGate99 likes this.
  7. PlayCreatively

    PlayCreatively

    Joined:
    Jan 25, 2016
    Posts:
    73
    Wow I've never thought of that! That's brilliant.
     
  8. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    484
    tmcdonald and jGate99 like this.
  9. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    8
    @JoshPeterson
    Is there any chance that roslyn compiler version will be updated again in Unity 2020.2 or Unity 2020.3 after Roslyn 3.8.0 will be released(as far as I understand this should happen with .NET 5 release in November)?

    It will be cool if Unity will support this version because it will have generator feature that could be helpful in many cases such as serialization or replacing reflection with generated code in performance-critical sections.
    Also, it looks like consuming generators will be pretty easy with Roslyn analyzers support in Unity 2020.2.

    P.S. I'm talking just about compiler update without updating C# version to C# 9:)
     
  10. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    4,692
    I don't think that this C# compiler update will make it to the 2020 release series. However we are working on support for source generators internally now - we see the benefits as well! I'm not sure yet when they will be available, but I do expect us to ship support for them at some point.
     
  11. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    484
    What about C# 7 ref returns for Unity APIs?

    If somehow that could work we could finally do:
    transform.position.x = 5;
     
    sean244 likes this.
  12. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,091
    Don't think that'd work, unless you can ref return an extern property?
     
  13. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    484
    No idea about marshaling but
    Code (CSharp):
    1. [MethodImpl( MethodImplOptions.InternalCall )]
    2. private extern ref Vector3 get_position_Injected( );
    doesn't show any errors.

    Obviously we have no idea what happens in C++ but maybe would work? I guess additional call would be required to apply position.
    Simple:
    Case A
    Code (CSharp):
    1. transform.position.x += 2;
    2. transform.ApplyPosition();
    vs
    Case B (current)
    Code (CSharp):
    1. Vector3 pos = transform.position;
    2. pos.x += 2;
    3. transform.position = pos;
    More complex:
    Case A
    Code (CSharp):
    1. ref Vector3 pos = ref transform.position;
    2. pos.x += 2;
    3. poz.z++;
    4. transform.ApplyPosition();
    vs
    Case B (current)
    Code (CSharp):
    1. Vector3 pos = transform.position;
    2. pos.x += 2;
    3. poz.z++;
    4. transform.position = pos;
    1. If ref returns would be supported Case B would still work correctly
    2. In Case A new Vector3 isn't created = faster code
     
    Last edited: Oct 21, 2020 at 2:18 AM
  14. TheZombieKiller

    TheZombieKiller

    Joined:
    Feb 8, 2013
    Posts:
    102
    Changing existing APIs to use ref returns would be a binary breaking change, even if it's not always a source-level breaking change. Any plugins that are distributed in .dll form would no longer function if they used any of the APIs in question.
     
    VolodymyrBS likes this.
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    5,091
    The change also would cause doing the wrong thing (forgetting transform.ApplyPosition()) to silently not do anything, instead of not compiling. So that's bad.
     
  16. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    484
    Yes this won't work out of box as replacement for older apis but rather could be added as new ones for better performance or/and cleaner code.
    Only my guess, maybe wouldn't be needed at all. There are similar apis like Texture2D.Apply so I don't see it as huge issue.

    What I'm trying to say is that there were some new features in C# that could be used to make "Unity MonoBehaviours" programming better. (or the plan is to go full DOTS and forget about them in 10 years?)
     
  17. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    471
    I am not sure about performance, but the need to call a method to set the position after getting its ref is no way cleaner than clearly modifying a copy and then setting the property.
     
    PlayCreatively likes this.
  18. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    484
    Just
    transform.position.x += 2;
    would be cleaner if possible.

    Let's call it new api something like positionRef. About performance: even when ApplyPosition would be needed there could be extern method that accepts array to speed it up. Could be 2-3x times faster:
    Code (CSharp):
    1. Transform[] transforms;
    2. void MoveAll
    3. {
    4.     for ( int i = 0; i < transforms.Length; i++ )
    5.         transforms[i].positionRef.x += 1;
    6.     transforms.ApplyPositions(); //Extension method for IEnumerable
    7. }
    Ignoring C# 7,8 etc, looking at simple apis I see they could be improved a lot in terms of performance so I guess we won't ever get anything advanced. For ex Transform.Translate:
    Code (CSharp):
    1. public void Translate( Vector3 translation, [DefaultValue( "Space.Self" )] Space relativeTo )
    2. {
    3.     if ( relativeTo == Space.World )
    4.     {
    5.         position += translation;
    6.     }
    7.     else
    8.     {
    9.          position += TransformDirection( translation );
    10.     }
    11. }
    Instead of sending Vector3 translation directly to cpp and calling one extern method it's a mess. Il2cpp output with even more problems:
    Code (CSharp):
    1. IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void Transform_Translate_mFB58CBF3FA00BD0EE09EC67457608F62564D0DDE (Transform_tA8193BB29D4D2C7EC04918F3ED1816345186C3F1 * __this, Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  ___translation0, int32_t ___relativeTo1, const RuntimeMethod* method)
    2. {
    3.     static bool s_Il2CppMethodInitialized;
    4.     if (!s_Il2CppMethodInitialized)
    5.     {
    6.         il2cpp_codegen_initialize_method (Transform_Translate_mFB58CBF3FA00BD0EE09EC67457608F62564D0DDE_MetadataUsageId);
    7.         s_Il2CppMethodInitialized = true;
    8.     }
    9.     bool V_0 = false;
    10.     {
    11.         int32_t L_0 = ___relativeTo1;
    12.         V_0 = (bool)((((int32_t)L_0) == ((int32_t)0))? 1 : 0);
    13.         bool L_1 = V_0;
    14.         if (!L_1)
    15.         {
    16.             goto IL_001e;
    17.         }
    18.     }
    19.     {
    20.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_2 = Transform_get_position_m40A8A9895568D56FFC687B57F30E8D53CB5EA341(__this, /*hidden argument*/NULL);
    21.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_3 = ___translation0;
    22.         IL2CPP_RUNTIME_CLASS_INIT(Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E_il2cpp_TypeInfo_var);
    23.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_4 = Vector3_op_Addition_mEE4F672B923CCB184C39AABCA33443DB218E50E0_inline(L_2, L_3, /*hidden argument*/NULL);
    24.         Transform_set_position_mB169E52D57EEAC1E3F22C5395968714E4F00AC91(__this, L_4, /*hidden argument*/NULL);
    25.         goto IL_0037;
    26.     }
    27.  
    28. IL_001e:
    29.     {
    30.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_5 = Transform_get_position_m40A8A9895568D56FFC687B57F30E8D53CB5EA341(__this, /*hidden argument*/NULL);
    31.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_6 = ___translation0;
    32.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_7 = Transform_TransformDirection_m6B5E3F0A7C6323159DEC6D9BC035FB53ADD96E91(__this, L_6, /*hidden argument*/NULL);
    33.         IL2CPP_RUNTIME_CLASS_INIT(Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E_il2cpp_TypeInfo_var);
    34.         Vector3_t65B972D6A585A0A5B63153CF1177A90D3C90D65E  L_8 = Vector3_op_Addition_mEE4F672B923CCB184C39AABCA33443DB218E50E0_inline(L_5, L_7, /*hidden argument*/NULL);
    35.         Transform_set_position_mB169E52D57EEAC1E3F22C5395968714E4F00AC91(__this, L_8, /*hidden argument*/NULL);
    36.     }
    37.  
    38. IL_0037:
    39.     {
    40.         return;
    41.     }
    42. }
     
unityunity