Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Unity C# 8 support

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

  1. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    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:
    150
    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
    AlejMC, Qbit86 and brunocoimbra like this.
  3. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    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:
    150
    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 :)
     
    AlejMC and brunocoimbra like this.
  5. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    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.
     
    GilbertoBitt and VolodymyrBS like this.
  6. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    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:
    94
    Wow I've never thought of that! That's brilliant.
     
  8. Kamyker

    Kamyker

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

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    @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:
    6,921
    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:
    1,085
    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:
    6,294
    Don't think that'd work, unless you can ref return an extern property?
     
    Walter_Hulsebos likes this.
  13. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    1,085
    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
    Walter_Hulsebos likes this.
  14. TheZombieKiller

    TheZombieKiller

    Joined:
    Feb 8, 2013
    Posts:
    265
    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:
    6,294
    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:
    1,085
    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?)
     
    Walter_Hulsebos likes this.
  17. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    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:
    1,085
    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. }
     
  19. MrPaparoz

    MrPaparoz

    Joined:
    Apr 14, 2018
    Posts:
    157
    A wild necromancer appears. Any ETA?
     
  20. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    2020.2 already has support for it
     
    PlayCreatively likes this.
  21. Rammra

    Rammra

    Joined:
    Jul 8, 2012
    Posts:
    54
    So do we have "default interface implementation" feature? I receive "Target runtime doesn't support default interface implementation" error in unity version 2020.1.9f1
     
    Kokowolo, Walter_Hulsebos and jGate99 like this.
  22. jGate99

    jGate99

    Joined:
    Oct 22, 2013
    Posts:
    1,936
    Its Not yet supported.
     
  23. MDADigital

    MDADigital

    Joined:
    Apr 18, 2020
    Posts:
    2,198
    I hope they leave that out, stupid feature
     
  24. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,921
    We don't have support for this yet, although we do plan to add it.
     
    Kokowolo, diesoftgames and Ghat-Smith like this.
  25. MrPaparoz

    MrPaparoz

    Joined:
    Apr 14, 2018
    Posts:
    157
    Pattern matching too, please.
     
  26. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    C# 8 pattern matching improvements already supported in Unity 2020.2. Just like all other features that don't require runtime support or new BLC
     
    slimshader likes this.
  27. MrPaparoz

    MrPaparoz

    Joined:
    Apr 14, 2018
    Posts:
    157
    I can't use it on 2020.2.b9 though, is it just me? Am I missing settings or something?
     
  28. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    Just tried myself on unity 2020.2b9 and all works just fine.

    Could you show pattern that doesn't work in your case?
     
  29. MrPaparoz

    MrPaparoz

    Joined:
    Apr 14, 2018
    Posts:
    157
    Code (CSharp):
    1. if(args is not AttackArgs attackArgs) return;
    "is not" doesn't work here for example.
     
    Qbit86 likes this.
  30. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    "is not" is a C# 9 feature, not C# 8
     
  31. Huszky

    Huszky

    Joined:
    Mar 25, 2018
    Posts:
    109
  32. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    I use this package too and it's a great tool. The only problem that I found with C# 9 is that il2cpp can't process property with init modifier. My guess that il2cpp doesn't expect modreq on property setter.

    upload_2020-11-17_17-51-45.png
    @JoshPeterson should I create a bug report or it's expected as long as Unity doesn't support C# 9?
     
  33. Huszky

    Huszky

    Joined:
    Mar 25, 2018
    Posts:
    109
    Did you add the missing type for init properties?
     
  34. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,921
    Go ahead and drop us a bug report. This is something that IL2CPP can likely handle now, as it is valid IL code.
     
    VolodymyrBS and Huszky like this.
  35. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    Yes, I've added missed type as internal in my project and libraries

    Code (CSharp):
    1.  
    2. namespace System.Runtime.CompilerServices
    3. {
    4.     internal static class IsExternalInit { }
    5. }
    6.  
     
  36. massivebacon

    massivebacon

    Joined:
    Apr 24, 2014
    Posts:
    27
    Hey Josh! Just wanted to check in with you all on where you were at with this. I made a thread here about source generators specifically, and just wanted to see if you had an idea of an updated timeline. I'm looking to use this suggested asset as a stopgap in the meantime, but would love to have official support as soon as possible because there are a ton of possibilities here!
     
  37. Jorhoto

    Jorhoto

    Joined:
    May 7, 2019
    Posts:
    99
    Hello, I was wondering if there was any progress on this feature implementation regarding c#8 default interfaces.

    Thanks!!
     
    Kokowolo, Ghat-Smith and jGate99 like this.
  38. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    I made a simple package that makes init property accessor works with il2cpp
    https://github.com/VolodymyrBS/InitAccessorRemover

    It just strips out all
    modreq(System.Runtime.CompilerServices.IsExternalInit)
    from all game assemblies before il2cpp run
     
  39. Huszky

    Huszky

    Joined:
    Mar 25, 2018
    Posts:
    109
    What is that dependency in the package.json?
    Code (CSharp):
    1. {
    2.   "name": "com.stuff.initremover",
    3.   "displayName": "Init Accessor Remover",
    4.   "version": "0.0.1",
    5.   "unity": "2019.3",
    6.   "dependencies": {
    7.     "nuget.mono-cecil": "0.1.6-preview"
    8.   }
    9. }
     
  40. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    it's Unity port of https://www.mono-project.com/docs/tools+libraries/libraries/Mono.Cecil/ . I used it to modify assemblies.
     
  41. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,921
    I don't have an update on official source generator support yet, sorry. We're still prototyping it internally, but I'm not heard about a release timeline yet.
     
    VolodymyrBS likes this.
  42. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,921
    Unfortunately no, we don't have any progress or release ETAs to report yet.
     
  43. EternalClickbait

    EternalClickbait

    Joined:
    May 5, 2019
    Posts:
    22
    So what's the status of C#8 support right now? I vaguely remember it being in one of the 2020 alphas but I can't really remember.
     
  44. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    65
     
  45. Jorhoto

    Jorhoto

    Joined:
    May 7, 2019
    Posts:
    99

    C#8 is not fully supported since Default Interfaces official answer is:

    I wonder if they are at least working on it, since it is a game-changer feature.

    Thanks ;)
     
    Walter_Hulsebos and Ghat-Smith like this.
  46. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,921
    Yes. we've started some work on this, but I don't have an ETA for a release yet.
     
  47. RogueStargun

    RogueStargun

    Joined:
    Aug 5, 2018
    Posts:
    296
    +1 for default interfaces.

    I keep cutting and pasting my interfaces code rather than using inheritance, and it has a bad code stink.
     
    Ghat-Smith, GilbertoBitt and Kokowolo like this.
  48. Kokowolo

    Kokowolo

    Joined:
    Mar 26, 2020
    Posts:
    52
    lol I can smell it from here! (I am in the exact same stinky boat, can't wait for default interfaces)
     
  49. Thaina

    Thaina

    Joined:
    Jul 13, 2012
    Posts:
    1,158
    Sorry to be honest but I seriously hate DIM feature in C# 8 and wish that it wouldn't exist in the language

    Also most of the things we need to use DIM for could be replaced with generic extension method

    Code (CSharp):
    1. interface IA
    2. {
    3.     int Do();
    4. }
    5.  
    6. static class A
    7. {
    8.     public static int DoSomething<T>(ref this T obj) where T : struct,IA
    9.     {
    10.         // somecode
    11.         return obj.Do();
    12.     }
    13. }
    14.  
    15. public struct B : IA
    16. {
    17.     public int Do() => return 0;
    18. }
    19.  
    20. ////
    21.  
    22. B b = new B();
    23. b.DoSomething();
    And if I remember correctly DIM in struct is pass by value, not by reference, so this should be aware of. Seriously, DIM should not be used by anyone in game industry
     
    Last edited: Dec 21, 2020
  50. Jorhoto

    Jorhoto

    Joined:
    May 7, 2019
    Posts:
    99
    It is just another tool, just stay away if you are afraid to gain its power :p. Of course, missuse is counterproductive.

    An use case:
    Imagina you want all the objects you spawn in your game to have an unique key/id and you assign it when they are created in a factory or such.
    With a default interface I would just create an interface like IObjectId and implement SetObjectId once for all. Right now if I just use the interface, I would need to repeat that same code for each implementation. Or use those smelly structs you suggest above. :)
     
    Kokowolo and sand_lantern like this.