The New Way of Calling Your Code in .NET 8 Is INSANE

Поділитися
Вставка
  • Опубліковано 22 гру 2024
  • Use code DOCKER15 and get 15% off the brand new Docker course on Dometrain: dometrain.com/...
    Become a Patreon and get source code access: / nickchapsas
    Hello, everybody, I'm Nick, and in this video, I will introduce you to an extremely powerful feature added in .NET 8 called the UnsafeAccessor. This feature aims to replace reflection on many levels and provide compile time performance but also allow for NativeAOT support.
    Subscribe to Dan: @danclarkeuk
    Workshops: bit.ly/nickwor...
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasG...
    Follow me on Twitter: bit.ly/ChapsasT...
    Connect on LinkedIn: bit.ly/ChapsasL...
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

КОМЕНТАРІ •

  • @gustavo-santos-dev
    @gustavo-santos-dev Рік тому +420

    This is probably a crime in +50 countries.

    • @dextergandini
      @dextergandini Рік тому +19

      It is also forbidden by Genebra convension!

    • @fakhrulhilal
      @fakhrulhilal Рік тому +3

      It means we can do anything what we want to be in any framework we’re using😂

    • @mattkins99
      @mattkins99 Рік тому +10

      OMG I don’t want to support this when I find it 10 years from now in a class that starts with the comment // Don’t touch, thar be dragons

    • @adambelos2157
      @adambelos2157 Рік тому +1

      Mama i am a criminal...

  • @clementdato6328
    @clementdato6328 Рік тому +312

    Finally, I can change the value of a static private read only field of a class in some other person’s code so that when another person use my library they will learn to read through all code before actually believing something is read only and private.

    • @Spid88PL
      @Spid88PL Рік тому +20

      INSANE

    • @fifty-plus
      @fifty-plus Рік тому +23

      Make sure your comments are totally off base too 😉

    • @IvanRandomDude
      @IvanRandomDude Рік тому +19

      Truly a gAmE-cHaNgEr

    • @erynmacdonald
      @erynmacdonald Рік тому +5

      WARNING *sarcasm* DETECTED

    • @phizc
      @phizc Рік тому +12

      You could have done that in previous versions of .NET too. It's just become a more pleasant coding experience to do so now 😛.

  • @KieranFoot
    @KieranFoot Рік тому +185

    I'm guessing this is part of getting rid of reflection for AOT...

    • @peanutcelery
      @peanutcelery Рік тому +11

      Yeah a lot is being set up for AOT like minimal apis.
      I feel like .NET 8 was the setup to make AOT more common for the next .NET versions.

    • @z0nx
      @z0nx Рік тому +4

      Interesting.. Is it because this can be done at compile time or something?

    • @renauddanniau676
      @renauddanniau676 Рік тому +3

      @@z0nx Exactly, I think they will combine this with source generator to generate code at compile time and not during runtime ;).

    • @RiversJ
      @RiversJ Рік тому +2

      Yup Native AOT plays a big role in .Net 8

    • @davidfowl
      @davidfowl Рік тому

      @@peanutcelerySomebody has been paying attention 🫡

  • @ElmoTheAtheistPuppet
    @ElmoTheAtheistPuppet Рік тому +12

    "The value is Nick" - You are the value mate appreciate your work

  • @mrsajjad30
    @mrsajjad30 Рік тому +10

    For this feature I must say "With greater power comes greater responsibility". Excellent demonstration. Thank you Nick.

  • @OlofLagerkvist
    @OlofLagerkvist Рік тому +18

    Great video, but I think one additional way of doing it should have been in the benchmark. That is, getting the MethodInfo object through reflection, then build a Func (or similar) delegate of it and then cache that delegate and call that instead of using the dynamic Invoke() method each time. Many years ago I changed that in lots of places in a code base that had a lot of reflection and improved performance a lot. The question now is how that compares to this new way of doing it!

    • @TheMonk72
      @TheMonk72 Рік тому +5

      I routinely use LINQ lambda expressions to build funcs for methods I'm accessing via reflection because they're faster than invoking a MethodInfo. And they work well with generics. Would love to see a speed comparison with this.

  • @woocaschnowak
    @woocaschnowak Рік тому +35

    With great power comes great responsibility.
    Don't use your power to change static readonly fields :D

  • @ristopaasivirta9770
    @ristopaasivirta9770 Рік тому +24

    Who would win: ten years of SOLID principles vs one unsafe boi?

  • @DennisFazekas
    @DennisFazekas Рік тому +72

    This seems really handy for unit testing, but I have concerns about using it in other contexts. As someone who writes a lot of infrastructure code, I find it crucial to restrict developers from accessing certain parts of the code.

    • @Kerbiter
      @Kerbiter Рік тому +14

      Sure, except when stubborn library devs only design something in the way they see consumers want, not what the consumers really want, and won't change anything and/or it's a lot of hassle to make them change, or it's just simply the way they're building their library.

    • @parcanapp1193
      @parcanapp1193 Рік тому

      nuke with mandatory .nuke folder immediately came to mind. I use .config for all configuration, and now have an extra folder. I will now go back and check if it is a static readonly as opposed to const by any chance. @@Kerbiter

    • @ehudv9276
      @ehudv9276 Рік тому +2

      Yes. haven't thought of that. thats actually a good use case.

    • @DennisFazekas
      @DennisFazekas Рік тому +1

      ​@@KerbiterI totally get where you're coming from! If the devs aren't providing what's really needed, then there might be a bigger issue going on. I'm all about sharing knowledge and having open code reviews. That way, everyone knows what's going on in the code and if any tweaks are needed, I'm more than happy to make them. I'd rather work together like this than have people go around my code, which could create headaches down the road. 😊

    • @TheRavenCoder
      @TheRavenCoder Рік тому +1

      Unit tests are definitely the most obvious use case. I've used reflection before on unit tests and it's a bit messy. I didn't want to make the methods public, because they should not be used directly by the application, but the logic needed to be tested as mistakes could cost the company millions of dollars. This feature would've cleaned that code up a bit.

  • @Demonata1223
    @Demonata1223 Рік тому +7

    I had a very good use case for this recently. We use private constructors for some of our objects because we want to tightly control how new things are made. But that inadvertently effected serialization. System.Text.Json cannot serialize with private constructors. Newtonsoft can. Thats an example of when something is private. but you may still need to access it externally in some situations. I dont want to open up something thats private in the code base but i still want to be able to serialize/deserialize it properly.

  • @dhochee
    @dhochee Рік тому +12

    "It's a pain ... where you don't want to be painful" is hilarious. I'm gonna steal that. Regarding the main topic ... I've used reflection to access private members before, but only when it was an abstraction designed to handle cases where you don't know the member names at design-time. This new feature doesn't help with that, so it's just ignoring the original developer's intentions. I suppose if you absolutely need to do it, having it be declarative with "Unsafe" in the keyword is better than hiding it behind reflection. Also much faster, of course, as you observed.

  • @PereViader
    @PereViader Рік тому +38

    Compile time performance is the best part of this ❤

    • @parlor3115
      @parlor3115 Рік тому +2

      I don't think it matters since you're mostly going to use this in tests where you run your methods a few dozen times each at most.

    • @BillyBraga
      @BillyBraga Рік тому

      You can probably get the same perf with reflection if you compile lambda expressions

    • @neppe4047
      @neppe4047 Рік тому

      ​@@BillyBragayeah I think he did something like that in another video, not sure why didn't in this one

  • @mindstyler
    @mindstyler Рік тому +51

    Will we get a dedicated in-depth video about the volatile keyword at some point? It's been 2 years already since you covered it briefly the last time.

    • @ludwintor4986
      @ludwintor4986 Рік тому

      absolutely agree

    • @thatcreole9913
      @thatcreole9913 Рік тому

      👀

    • @tarsala1995
      @tarsala1995 Рік тому +3

      Ideally with some multithreaded examples

    • @IronJmo
      @IronJmo Рік тому

      It's funny seeing a comment about this because I just used this recently on a project for the first time.

    • @renauddanniau676
      @renauddanniau676 Рік тому +3

      Volatile is not really complex, you should read what MSDN has on it and you will easily understand how to use it :).
      You need to have a good understanding about how multithreading works and what are L1, L2 and L3 caches.

  • @benjaminclehmann
    @benjaminclehmann Рік тому +18

    I'm wondering if this is safer, for example if the method doesn't exist or the signature is incompatible would this fail at compile-time? Because in that case this seems like a massive win, especially for those who are committing these types of sins on code they don't have access to.

    • @marton-vincze
      @marton-vincze Рік тому +2

      I highly doubt that compile time checking for private field are possible, the input parameter for the accessor can be typed as an interface or an abstract class where some implementations will possibly have this method/field

  • @ehudv9276
    @ehudv9276 Рік тому +2

    In "powerful" you mean "I've done mistakes in the design so I have to break encapsulation, but I want some syntactic sugar on it so my boss will think I'm doing a good job"
    You'd be better shooting your own foot.

  • @anm3037
    @anm3037 Рік тому +2

    The first place I will use it is to grab the array under List

    • @mbenoni7397
      @mbenoni7397 Рік тому

      You can already do that with CollectionsMarshal.AsSpan()

  • @TheRicherthanyouguy
    @TheRicherthanyouguy Рік тому +33

    I’m so mixed on this I can definitely see value from a unit testing perspective because there are codebases that have private methods that really have too much going to be private. Which really just needed to be rewritten. I strongly dislike accessing things that should be private purposefully but I also love the performance boost

    • @leerothman2715
      @leerothman2715 Рік тому +1

      I can see it being useful for testing DDD classes that have immutable properties on a test fixture setup. Not sure I’d use it for executing private methods though. Unit tests should be decoupled from the implementation of the subject under test.

    • @TheRicherthanyouguy
      @TheRicherthanyouguy Рік тому

      @@leerothman2715 agreed I think we are all mostly on the same page of this can be useful but only in rare or extreme situations.

  • @PeterOeC
    @PeterOeC Рік тому +2

    My mind was blown once, when my colleague showed me how to make a self-updating const in JavaScript.
    Now you're showing me that private properties can be changed from outside classes 🤯
    I need to sit down

  • @Tolg
    @Tolg Рік тому +7

    This is fantastic! I think it will allow for me to make more things private, and still have them testable. I’ve had so many cases where I couldn’t make something private because I needed to expose to my testing framework.

    • @filadog
      @filadog Рік тому

      @@jkdmyrs how do you test private async event handlers on UI, say WinForms? This will be handy in such cases when in memory UI testing is required. Faking button clicks and such. In my place we use MVP pattern for UI code extensively and have a custom framework for testing presenters with specflow. It's real nice to work with, you just say User clicks button "Download". And reflection magic calls private event handler for that button in the presenter. Might use this feature in the future instead.

    • @jangohemmes352
      @jangohemmes352 Рік тому +1

      The tests are screaming at you that you aren't designing your code and tests right. NEVER test internals. They are internal so that they are free to change without breaking tests

    • @Tolg
      @Tolg Рік тому +1

      I know this is a contentious topic and I don't intend to start a, how to correctly write and test your code correctly war, here in Nick's video comments. In general, I do agree with you. You should only need to test your public interfaces. However, even for backend code, sometimes in libraries there is good reason unit test certain methods such as utility methods which may have flexibility extended to them to address future needs or test integration code that is not conducive to easily being mocked out without leaking abstraction. You could abstract out such code, mark things as internal and add InternalsVisibleTo to your library to allow its testability, but I feel that modifying a base library in such a way just to be able to test, is not exactly proper either. So I will certainly experiment with this new capability to help improve my code and my code's testability.

    • @paulcarlton598
      @paulcarlton598 Рік тому +1

      I've dealt with this too, to get around it, I'll create a testing interface for the object only to expose the needed methods through that testing interface only. It's a way to keep the intent of the contract while being able to cast to a testing interface for the unit test suite. This functionality though actually gets rid of that need which I definitely appreciate, because in all honesty I feel like my workaround, although stays clean, is kind of a workaround.

  • @modernkennnern
    @modernkennnern Рік тому +4

    How does this interact with string.Empty? I remember reading that it used to be changeable but they fixed that. Does this reintroduce the possibility of changing it? It's a static readonly after all

    • @nickchapsas
      @nickchapsas  Рік тому +8

      I tried. You can't set its value since its marked as a JIT intrinsict

    • @modernkennnern
      @modernkennnern Рік тому

      @@nickchapsas that's great

  • @BillyBraga
    @BillyBraga Рік тому +46

    Personally, I think a valid use case is when you are using a nuget package that doesn't expose the functionality or configuration required for functioning properly in your app.

    • @PyronTheMage
      @PyronTheMage Рік тому +6

      I've had this problem a number of times. One example has been when a package is serializing data using its own internal serializer and does not provide any configable settings or callbacks. This has prevented me from using custom logic required by an app I was working on. It's good to open up an issue on their pull request but that could delay your code from going live or completely block you if they choose to ignore your issue for years.

    • @phw1009
      @phw1009 Рік тому +1

      That will be very useful, especially on nuget packages those not maintained anymore.

    • @noon9548
      @noon9548 Рік тому +1

      ​@@phw1009how would you improve the packages that not maintained anymore? Do you mean changing inners of packages or extending functionality?

    • @davidmartensson273
      @davidmartensson273 Рік тому

      @@noon9548 It could be enough to be able to access some internals to be able to work around a problem.

  • @IllidanS4
    @IllidanS4 Рік тому +3

    I love nothing more than ways to break guarantees in a runtime (but readonly fields were modifiable before using... ways). Anyway I feel your performance benchmark is missing the case when you call the method via a delegate, as that is the "proper" way of binding to it (as opposed as Invoke having to perform argument unboxing and whatnot before getting to the method). Perhaps throw a function pointer into the mix as well.

  • @markhenderson5283
    @markhenderson5283 Рік тому +17

    My experience with code using refection is it is either doing something really powerful and useful or something stupid. It is usually always the second one.

    • @anonimzwx
      @anonimzwx Рік тому +3

      Imo reflection is useful for testing private things or to call generic methods with multiples types.

    • @mz00956
      @mz00956 6 місяців тому

      I use reflection in my GPT library.
      GPT can call "tools" but need them in json format, and then calls them in json format.
      What I accomplished for my library is that I can just write
      //static
      model.addTool(() => MyClass.MyMethod);
      //instance
      model.addTool(mc => mc.MyMethod2);
      And converting them to json as well as finding and invoking them again after getting a json call happens in the background. This is saving so much time.
      (Though I only accept public methods, didn't even think private ones would work)

  • @DemoBytom
    @DemoBytom Рік тому +1

    This is gonna be used in source generators so much, and I'm 99% sure that was the main reason the feature got introduced to the runtime.
    In pre .NET 8, to extend a class with source generator you have to make the class partial, and then have the generator create the extension in the class' partial.
    With this feature we will be able to extend non partial classes, just as if they were, without bloating said class with the generated methods, you might NOT need outside of what the generated code was for. This is an amazing, though very dangerous, thing indeed.

  • @jc_alpha
    @jc_alpha Рік тому

    4:17 “It doesn’t take a genius to understand that having to write something like this *every single time* is very very tedious”
    You make it sound like this is the first thing we do every morning before standup.

  • @rreiter
    @rreiter Рік тому +1

    5:50 Yes, I want to easily access private members that I shouldn't be using, and, oh, 11:00 so much for readonly, ... this will be fun (especially if it is performant, lol). I wish the Law worked like this: "Yes, Officer, I broke this law, but I said a special word when I was doing the bad act, so that makes it ok and it's all good."

  • @PeteSauerbier
    @PeteSauerbier Рік тому +2

    Can this be done with extensions as well?

  • @bluesillybeard
    @bluesillybeard Рік тому

    Surprisingly often I find myself needing to call a method that isn't in the public API of a library.
    It's usually a method that should be public, but the creator of the library thought otherwise or just accidentally wrote the wrong keyword.
    Using this will be a lot easier than using reflection, so I consider it a bonus. Plus, I imagine it works with AOT too (since it links at compile time) to make reflection-like things possible for anyone using AOT.

  • @sergeynosov8180
    @sergeynosov8180 Рік тому +2

    Is this a good use case for private members that are made public for no other reason but to make unit testing easier?

    • @osono-io
      @osono-io Рік тому

      I came to the comments to post this same use case. This will indeed be useful.

    • @SylvanFeanturi
      @SylvanFeanturi Рік тому

      No. The correct solution is to only test public API.

  • @dadcraft9949
    @dadcraft9949 Рік тому +5

    Did you know that if you cache an open instance delegate instead of the MethodInfo you can get the same performance with "reflection" (actually with a delegate) that you see with the UnsafeAccessor? All you have to do is call CreateDelegate with a type argument that accepts the target type as a parameter, in this case Func.
    var testInstance = new Example();
    var openDelegate =
    typeof(Example)
    .GetMethod("Method",
    System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
    .CreateDelegate(null);
    var name = openDelegate(testInstance);

    • @nickchapsas
      @nickchapsas  Рік тому +8

      I do, and I have made a video on it. It's not quite the same performance and it can't be JITed the same way either plus its usecases are quite more limited, but it's very close

    • @SimpMcSimpy
      @SimpMcSimpy Рік тому +8

      That is not the point of this feature. This was added for AoT compilation. Reflection in general doesn't work well with AoT, this is sort of quick patch for such cases.

    • @dadcraft9949
      @dadcraft9949 Рік тому

      @@SimpMcSimpy yeah I'm not recommending anyone should choose this approach (for anything ever, really). I just think it's worth knowing about if you are stuck with legacy reflection code and need to optimise it. It's odd to me how many performance focussed articles and videos will measure reflection performance without touching on how trivial it is to boost the performance to match a virtual method call in many situations.

    • @SimpMcSimpy
      @SimpMcSimpy Рік тому

      @@dadcraft9949
      You are correct. I saw that in many articles :)

    • @DynamicalisBlue
      @DynamicalisBlue 7 місяців тому +1

      Thanks for this. I'm using cached reflection for something at the moment. I also thought of creating a delegate for performance purposes, or at least some reference to an actual native method but I couldn't figure out how to do it. I didn't know there was a method called CreateDelegate this entire time.

  • @diadetediotedio6918
    @diadetediotedio6918 Рік тому +2

    11:55
    Yeah, but you can do it with unsafe (in fact, something very much more unsafer than to change a readonly field, but to change the string contents itself)
    Reflection was not able to do it before? I have an impression that it could

    • @nickchapsas
      @nickchapsas  Рік тому +3

      You can do anything with unsafe but no, you couldn't do it with reflection

    • @diadetediotedio6918
      @diadetediotedio6918 Рік тому

      ​@@nickchapsas
      Hmmm, thanks. That probably was a mistake or false memory of my part

    • @vyrp
      @vyrp Рік тому +2

      After some quick tests, I see that trying to modify a static readonly field in .NET 7 throws FieldAccessException, but works fine in .NET Framework 4.8

  • @metaltyphoon
    @metaltyphoon Рік тому +4

    If all the reflection based methods on the BLC gets to use this feature under the hood, then most reflection code can be AOT compatible

    • @Alex-ABPerson
      @Alex-ABPerson Рік тому

      That doesn't really work because the parameters on the attributes need to be _constant_ - while reflection obviously only knows what strings it's getting when it's executed.

  • @leowong9885
    @leowong9885 7 місяців тому

    Thanks for introducing this new feature. I am wondering what is the motivation behind it. If authors of classes want people to have access to protected/private methods/fields, would they have not marked the functions or fields public instead?

  • @B1aQQ
    @B1aQQ Рік тому +2

    So did they add it with direct use in mind, or is it mainly for source generators?

    • @nickchapsas
      @nickchapsas  Рік тому +4

      It's mainly for NativeAOT

    • @GumbootMan
      @GumbootMan Рік тому

      Yes, it's primarily for source generators. This feature is extremely fragile when used manually (since it depends on the class internals), but when used in a source generator the UnsafeAccessor calls can be tailored to exactly match the class internals, whatever they might be. For example, it could be used in a source generator to serialize all the private members of some arbitrary class.

  • @RobinJohannesson
    @RobinJohannesson Рік тому +2

    That is awesome!
    But how does it work underneath? How do UnsafeAccessor attribute know what type I am using? And what happens if I have two instances of the same class, but with different values? And what happens if there is multiple functions with the same name?

    • @TheMonk72
      @TheMonk72 Рік тому

      My guess is that the "extern" keyword is doing all the heavy lifting here. In previous versions of the language it creates code to call a library method. Looks like they did something similar here except the compiler just inserts code to call/reference things you normally wouldn't have access to.

    • @GumbootMan
      @GumbootMan Рік тому +1

      @@TheMonk72 Good guess, but nope, the compiler is not involved. Rather, this feature is implemented in the JIT -- it sees the [UnsafeAccessor] attribute and generates the appropriate IL to call the method or get a reference to the field. If the target method/field is not found, then it generates code to throw an exception instead. (This actually explains why getting the name wrong is not a compiler error -- can't trigger a compiler error if the compiler is not involved!)

  • @gliaMe
    @gliaMe Рік тому

    Regarding the code mess of the reflection solution, I had an extension method for the reflection calls in a project. Worked for any property of any class.

  • @shavais33
    @shavais33 26 днів тому

    So you can actually use this across assembly boundaries? I remember years back sometimes running into issues with certain external libraries/frameworks that *almost* did what I needed, but not quite, and it appeared to be because of unnecessarily restrictive access modifiers. Ah, it was so frustrating. I wonder if this would've helped me, if I'd had it at the time..

  • @MikeZadik
    @MikeZadik Рік тому +1

    This can help to fit or fix problems in 3rd party components when you have access to the source code, but you don't want to build your own version just to change that one read only value to activate tls or something like that.

  • @fredericbrown8871
    @fredericbrown8871 Рік тому +1

    I think it can help write more robust and cleaner code when you have to use reflection anyway (assuming type safety is enforced for the extern methods of the caller class). However I would be warry of accessing, let alone setting (especially if read only), private members. I guess there might be edge cases I'm not thinking about that justify it, but unless it's for the purpose of diagnostic it feels antithetical to encapsulation and the safety it provides and looks like looking for trouble to me.

  • @GTSJoeG
    @GTSJoeG Рік тому +1

    Which editor is he using? Doesn't look like Visual Studio nor VS Code.

  • @rmcgraw7943
    @rmcgraw7943 3 місяці тому

    Does this work for with the BCL too? If so, then it makes all of the BCL virtual, seemingly.

  • @kumailn7662
    @kumailn7662 Рік тому +1

    what is the purpose of private method and field after this?
    by the we have this already in Java years ago

  • @onman999
    @onman999 Рік тому +2

    I guess this enables you to test private methods (which isn't really necessairy unless you develop BIG methods with multiple tasks combined). Is there any other use case for this?

    • @77Zamien
      @77Zamien Рік тому

      Exactly my first thought as well

    • @lordmetzgermeister
      @lordmetzgermeister Рік тому

      There are many ways to write code but only a few produce code that is easily testable. This can be quite helpful for badly written legacy code I suppose.

    • @Daniel15au
      @Daniel15au Рік тому +1

      If your methods have multiple tasks combined, they're too large and should be broken into smaller methods. Ideally private methods are implementation details that don't need to be tested on their own.

    • @GumbootMan
      @GumbootMan Рік тому

      FYI Microsoft is planning to use this feature to enable the built-in JSON serializer to deserialize private members in source generation mode (in a way that is compatible with NativeAOT).

    • @onman999
      @onman999 Рік тому

      @@Daniel15au My point exactly.

  • @briantaylor7695
    @briantaylor7695 Рік тому

    Any idea if it works with Local functions, if so that would be so useful for unit testing

  • @Misteribel
    @Misteribel Рік тому

    Is this a compiler feature or a dotnet feature? That is, does it get desugared by C#, or does it work with any language?
    PS reflection is usually used with names or other member or class features nor known at compile time. Once you know it statically, like in your example, you can use existing tricks to make it as fast (cache and use delegate) as a normal method call. Of course, this approach makes certain use cases simpler, but does not replace reflection.

  • @99aabbccddeeff
    @99aabbccddeeff Рік тому

    It's really helpful feature in some rare cases. Thanks for the sharing.

  • @the-niker
    @the-niker Рік тому +4

    Ooof.. I sense a major rewrite in my future. How is the compatibility with older .NET7 or 4.8, can you multitarget to those and compile or do you have to #if NET8_0_OR_GREATER and write the reflection version anyway?

    • @Daniel15au
      @Daniel15au Рік тому

      This is likely a compile-time feature, meaning it should be possible to compile to a .NET 4.8 target. I haven't tried it though. It'd likely just compile to the old-style reflection code when targeting older runtimes.

  • @SlackoJacko
    @SlackoJacko Рік тому

    Nick's on a mission to one up each .Net 8 video with something having even more terrifying possibilities.

  • @SchadenNZ
    @SchadenNZ Рік тому +7

    This makes me wonder if it would have a place in testing. Sometimes I want to access a value that should be private so as a work around I would change it to internal and allow internal access to the test project. With this new code I'm assuming i could leave it private and still access it?

    • @AlFasGD
      @AlFasGD Рік тому +17

      You should never perform behavioral testing, like exposing its inner components and testing how they are adjusted behind the scenes. You should only care about the exposed side effects, your current public state and whether your code does what it is supposed to do. If you feel like you have to use this during testing, you are either doing something unsafe in nature, or you are simply not doing something good and need to test something more important.

    • @rab1d78
      @rab1d78 Рік тому +4

      @@AlFasGD While I agree, I can think of a use case for testing. Large old crappy code bases that weren't written with tests in mind. Classes that don't follow SRP, but have private methods that go and gather data and such. If they work well enough to not warrant a rewrite but could use some unit test coverage... I would want to think about it longer but the potential...

    • @AlFasGD
      @AlFasGD Рік тому +1

      @@rab1d78 Legacy codebases are always the exception to the rule. That's the scenario that this feature is primarily intended for; legacy systems and libraries that barely hold things together. Writing tests even for observational purposes could also help ensuring that you know what to expect in certain scenarios.

    • @ronsijm
      @ronsijm Рік тому

      @SchadenNZ yes sure. What I usually do is create a source generator for my test project, that creates private method accessors through reflection.
      With this new UnsafeAccessor you could do something similar

    • @SchadenNZ
      @SchadenNZ Рік тому

      @@AlFasGD Theory is all well and good but try working on a code base that is over a decade old, was written without any consideration for testing, has been modified so many times that no one knows the purpose of large sections of code, serves half a BILLION requests a month with a $10k per minute SLA penalty --- and the ticket wants you to add a few tests to increase confidence all the while changing as little as possible.
      Don't expose inner components is so far down my list of priorities that I'd have to take an elevator to get there. 🤹

  • @kelton5020
    @kelton5020 Рік тому

    I dig it. I think this makes a lot of sense for them to iterate and improve all aspects of the ecosystem, instead of just neglecting the unsightly parts.

  • @iam3yal
    @iam3yal Рік тому

    I've read various comments that essentially say that this kind of mechanism defeats the purpose of access modifiers so I hope my explanation is going to help and clarify some of that confusion or misconception, as strange as it might sound to some people access modifiers were never designed as a permission system but as an encapsulation mechanism to control the visibility of different parts of the system not to prevent it, this concept exists in programming mainly for maintainability and usability, e.g., the engine of a car is hidden within the engine compartment whereas the hood is restricted. Point is this mechanism of .NET 8 to access members (and soon types) changes nothing people that wanted/needed to access your code before could always do it through reflection or more hacky alternatives depends on the language/platform so if people are using access modifiers to restrict developers from code that is actually sensitive data then they are using the wrong tool for the job.

  • @Noceo
    @Noceo Рік тому +1

    This is gonna result in some fun code bases to maintain in 3-5 years from now...
    (I do see a case for debugging, learning and fiddling around with stuff, but never for production code)

  • @hichaeretaqua
    @hichaeretaqua Рік тому +1

    I think this is a good approach to access private things if there is no other way. But I'm glad we have code reviews and hope tampering with private things never gets through...

  • @Dimencia
    @Dimencia Рік тому +13

    This isn't the worst thing you can do in C#; most of a dev's job is finding that balance between terribleness and efficiency. Always nice to have more tools in the kit

    • @ForeverZer0
      @ForeverZer0 Рік тому +3

      I agree. More power is never a bad thing, so long as their is sufficient barriers in place from doing it accidentally. As long as the user must do "unsafe" (I hate this term) explicitly, there is nothing wrong with a language supporting it.

    • @memes_gbc674
      @memes_gbc674 Рік тому +1

      a wise man named terry davis has mentioned this many times

    • @ForeverZer0
      @ForeverZer0 Рік тому

      @@memes_gbc674 TempleOS was glorious: an entire OS where everything occurred in kernel-space. The "muh safety" cult would lose their minds if they knew.

  • @Andrei15193
    @Andrei15193 Рік тому

    Had this issue with the Azure Table Storage stub I was working on, there was no public constructor for creating a result set. I had to use reflection to get an instance although it is only a DTO. Very annoying as everything else was accessible.

  • @timarheit7272
    @timarheit7272 3 місяці тому

    That's great, but what if you also cannot access the object type directly?

  • @knifekitty_ls
    @knifekitty_ls Рік тому

    can you get static field of Method() and replace it with new Func that has completely different implementation?

  • @peroyhav
    @peroyhav Рік тому +1

    Have had to access internal methods from external APIs previously, this would have made that experience easier. Looks a lot like calling external APIs in the.net 3.5 days, so its at least consistent with how interop code was written to get access to hardware drivers etc.

  • @jacquesdemolay2699
    @jacquesdemolay2699 Рік тому +2

    Could you give examples of when we MIGHT need to access a private method ?

    • @markmidwest7092
      @markmidwest7092 Рік тому +2

      Unit tests

    • @arjix8738
      @arjix8738 Рік тому +1

      One great example is modding Unity games!
      Although when modding you'd probably use a fake assembly as ur reference, so the compiler thinks that everything is public (lookup NStrip)
      (yeah this didn't age well)

    • @stxnw
      @stxnw Рік тому

      @@arjix8738 "so the compiler thinks that everything is public" are you talking about injecting code into the unity process? because I still have to use reflection to access private variable if I am injecting.

    • @toebeann
      @toebeann Рік тому

      ​​@@arjix8738Assembly Publicizer has been my go-to for the same purpose, but I see NStrip is made by the BepInEx creator so I will give it a look too!
      Do somewhat wish this new API existed back in .net 4 or something so we could use it instead of publicizing all members (I much prefer the idea of only publicizing those members I need rather than everything - less footguns that way!)

  • @JustArion
    @JustArion Рік тому +1

    I would have preferred an Attribute to this. Though it is still a step in the right direction. Roslyn has/had something similar with the [assembly: IgnoresAccessChecksTo("assemblyName")] Attribute. Wonder if the boilerplate can be source-genned away or some notification can be shown if the member you're trying to target no longer exists.

  • @F1nalspace
    @F1nalspace Рік тому

    This is awesome! I had some cases in the past, where i had to use reflection to get a private value out - and it was very slow.
    Great that there is now a tool for doing the same thing - and more, for compile time performance. I like it ;-)
    Thanks for sharing.

  • @BGroothedde
    @BGroothedde Рік тому +1

    Nice, another footgun! It's almost C++ now :D

    • @SylvanFeanturi
      @SylvanFeanturi Рік тому +1

      I think that the only thing that's missing is pointer arithmetic?

    • @DynamicalisBlue
      @DynamicalisBlue 7 місяців тому +1

      I wish.
      I'm currently on a C++ and a C# project. Whenever I'm on the C++ project, I miss reflection. Whenever I'm on the C# project, I miss being able to do what I want.
      (The thing I hate the most about C#, is not being able to treat a nullable as a bool. Let me do `if (someNullable)` Please!

    • @antred11
      @antred11 4 місяці тому

      @@SylvanFeanturi To be fair, pointer arithmetic is generally unnecessary in truly modern C++ (I'm not talking 90s-style C++ here).

  • @DivingDeveloper
    @DivingDeveloper Рік тому +1

    Thank you for this, Nick. Concise and helpful as always. I can see the use - as you demonstrated - clean and fast running, if potentially dangerous code.

  • @tmarkovski
    @tmarkovski 8 місяців тому

    Wasn't this (type analysis in extern DLL) always there? How is it new to net8?

  • @ja_mcito
    @ja_mcito Рік тому +6

    Maybe it's just for use in highly optimized libraries

    • @SimpMcSimpy
      @SimpMcSimpy Рік тому +3

      Perf is only one reason but no the main one. It's mostly for AoT and code trimming scenarios where reflection can't figure out what to take out or what to leave.

    • @Daniel15au
      @Daniel15au Рік тому

      ​@@SimpMcSimpyThe property name is still a string though, rather than a static reference... Will this help with AoT?

  • @warny1978
    @warny1978 Рік тому +1

    I see a use case. I used that use case many times.
    I have a method that I need to unit test. I also do not want developpers to directly use that method so I make it private or protected. This allows to do that without complicated code.
    Another Use Case: I have created an abstract class that keeps track of field changes. This is done with backing field not visible to the end class. This allows me to access these hidden fields without permitting a developper to use it directly.

  • @Neonalig
    @Neonalig Рік тому +7

    Probably going to be eons before Unity updates to .NET 8 (if its even around then given their current dramas), but when it does, this will really change editor scripting for the better. So many useful methods and classes are needlessly internal or private, resulting in tons of required (cached) reflection or even IL emission to do anything even remotely advanced.

    • @mysteriousmop7686
      @mysteriousmop7686 Рік тому +5

      ...and if we're lucky, they'll only charge developers $150 per .NET major version!

    • @TheGalantir
      @TheGalantir Рік тому

      Unity is dead even with their new price change as trust is not adressed.
      Try Stride instead they're reasonably up to date with their .NET version.

    • @DynamicalisBlue
      @DynamicalisBlue 7 місяців тому

      Some people still haven't moved to Unreal/Godot yet?
      Unreal exposes a lot and lets you override almost everything, and for the very few things you can't, you can modify the engine directly.
      Can't speak for the exposedness of Godot but at least it's open source so you can still do what you want.

  • @darthruneis
    @darthruneis Рік тому +1

    I have an actual use case for this that I'm dealing with right now, but unfortunately its on Net Framework, so this wouldn't really apply. Its on an old MS library for OIDC support on OWIN, so its a good use case in the sense that the code is very mature and very unlikely to change, and also the implementation is not extension-friendly (its all private/internal, not protected and/or virtual).
    So, I guess I'm stuck with the reflection code (or copying their implementation verbatim from github).

  • @mclifeleader
    @mclifeleader Рік тому

    As a developer and an engineering manager, I can say that as helpful as this maybe it wreaks of code smell!

  • @blaaaaaaaaaaaaargh
    @blaaaaaaaaaaaaargh Рік тому

    Will this work in external code? Ie 3rd party plugins you'd otherwise have to have a license for and skip their license checks?

  • @skeggs
    @skeggs Рік тому

    Love the captioning of your opening line at 0:00
    "hello everybody I'm naked in this video.." 😇

  • @benniebees
    @benniebees Рік тому +1

    But why does it need you to pass an instance when you're accessing a static member? edit.. right.. it needs to know on what type..

  • @dimitris470
    @dimitris470 9 місяців тому

    WoW this is God-sent. I've been writing a state-machine/workflow library kind of thing, and the fastest I managed to access properties etc was with compiled expressions, which still was a lot slower than direct access. Too bad it does not support Generics yet... it would simplify many things

  • @tanglesites
    @tanglesites Рік тому +1

    This breaks encapsulation. What use cases does this have?

  • @TohidAzizi
    @TohidAzizi Рік тому

    Nice! it is something that I probably only use in my unit tests!

  • @nickbarton3191
    @nickbarton3191 2 місяці тому

    I saw Zoran Horvat do something like this for field mapping with Entity Framework. It being private makes it clear that it's not part of the model API was his reasoning.

  • @jimwithheld7217
    @jimwithheld7217 Рік тому

    So I have a private var inside my class, and I 100% trust it only holds 1 of 4 values that my method calls allow. But now it might be any other value too. How on earth is safe and tested code possible with those kinds of rules at play?

    • @toebeann
      @toebeann Рік тому +2

      If someone messes with your code and changes the private value to something unexpected and it breaks, it isn't your job to support them beyond saying "yeah, don't do that."
      Using reflection and other means to access private members have existed in .NET for a long time, there is nothing you are seeing in this video that wasn't already possible some other way. It's just a new API for doing it with the same performance as it it were public, and that's already possible, via even more unsafe means.
      So, treat private the same way you always have. Most users of your libraries etc. won't ever think about trying to access private members, and those that do will almost never come crying to you about it if they break something in the process, because they should know better.

  • @AlFasGD
    @AlFasGD Рік тому +3

    I got ill just by hearing your voice, get well soon Nick

    • @nickchapsas
      @nickchapsas  Рік тому +1

      Damn I didn't know people would notice! Thanks ❤

  • @qub1n
    @qub1n Рік тому

    Is compiler able to detect missing method in compile time? The most pain to me on reflection is maintaining someone's else code based on reflection.

  • @JohnAndrews_nz
    @JohnAndrews_nz 8 місяців тому

    I wouldnt use this for private, but the performance boost, often we use reflection on a "object" where we knnow that object has a property/method, that doesnt have a common interface to expose it, that this could then be used to call that public property/method to get/set that value.

  • @kconfesor
    @kconfesor Рік тому +3

    This will be really useful for mocking libraries and testing 😊

    • @cgeorgescu72
      @cgeorgescu72 Рік тому

      Will be really useful for messing up with the juniors in your team: overwrite their readonly int=1 then let them struggle a day to understand why it doesn't pass the test.

  • @frankquednau889
    @frankquednau889 Рік тому +2

    Kinda OK with having this. Just yesterday I had to use some internal setters for tests involving types of the blobstorage lib. But allowing this to break promises like static readonly seems to me *absolutely bonkers*. Really.

  • @jongeduard
    @jongeduard Рік тому +4

    I would say it opens more ways of unit testing, and especially performance testing (since it no longer has to be slower than public calls) of your own projects.
    Generally I also really like the better philosophy that in the end a compiler should not actually limit you to do things, but can give you full control if you really demand so.
    In the end it's up to YOU as a developer to make good coding decisions. A compiler should not be needlessly childish, but help you by making make guidelines clear. By disabling private access with the normal way of calling, the message is already clear, you should normally not want to do it, since it defeats purpose.

  • @nocturne6320
    @nocturne6320 Рік тому +3

    This is an amazing feature, only a shame that you haven't included a benchmark for a compiled lambda as well.

    • @AlwaresHUN
      @AlwaresHUN Рік тому +2

      Its great till my colleagues don't know about it :D

  • @trevorprice2490
    @trevorprice2490 Рік тому

    Thinking ahead to use cases I might encounter, I'd probably add an "Unsafe" suffix to all my "Caller" method names just to add a reminder to any users about what's potentially happening behind the code

  • @tyronestephen4263
    @tyronestephen4263 Рік тому

    Can you use this to do unit tests on private methods?

    • @gbjbaanb
      @gbjbaanb Рік тому

      why would you want to? A class is the unit, private methods should not be tested like that - you test the public API, the only one you use.

  • @tiendatniit
    @tiendatniit Рік тому

    Could someone help me to explain why 10 years before They referred to use the of reflection?. It's really painful to debug in the huge system.

  • @lordmetzgermeister
    @lordmetzgermeister Рік тому

    I assumed the compiler generates the references at compile time, but according to documentation the implementation is generated by the runtime.
    How the heck is it so fast then?

  • @aispweelun
    @aispweelun Рік тому

    How are you able to set the value of Field by calling the function and then assign 'John' to it? Could someone explain this?

    • @nickchapsas
      @nickchapsas  Рік тому +2

      Because it’s a reference due to the use of the ref keyword

    • @aispweelun
      @aispweelun Рік тому

      @@nickchapsas i see. So having the ref keyword would allow the function achieve the same purpose as something like propertyInfo.SetValue(Example, "Nick")?

  • @user-nq8ln7ps7x
    @user-nq8ln7ps7x Рік тому +1

    I wonder if it can be used with generic methods/interfaces as well? It's always pain when some libraries only expose generic methods, so you have to live without polimorthism and specify concrete generic interfaces, or create some non-generic decorators with lots of delegate generation / caching / casting. For example: Kafka producers in MassTransit

    • @alexisfibonacci
      @alexisfibonacci Рік тому

      I instead create a lightweight generic wrapper with non-generic interface implementation. Instantiate that wrapper per type and cache the wrappers in a dictionary with the Type as the key. When needed, do the lookup, call to the instance to get the work done, and move on.
      But I guess .NET 8 can simplify all of that and we get to delete some code.

  • @MichaelMortensen
    @MichaelMortensen Рік тому

    Wonderful insights, thanks!

  • @derhinek
    @derhinek Рік тому

    The only thing I can think of, where it makes sense to access private members would be in tests. Microsoft has/had an way for this in their own test framework using a class I think called PrivateObject.

  • @MrDragnil
    @MrDragnil Рік тому +1

    Could be useful for certain testing situations as well.

  • @that_rendle
    @that_rendle Рік тому +15

    This would be even better if there were a compile-time check that ensured the target member exists, which does seem like something it could do.

    • @mortenmoulder
      @mortenmoulder Рік тому

      Instead of "Method" you could use nameof() I guess. Which you also should, because if your method ever changes name, your unsafe accessor breaks. Small improvement over Nick's example, but that's about it

    • @mindstyler
      @mindstyler Рік тому

      There is

    • @matthew_jackson
      @matthew_jackson Рік тому +1

      ​@@mindstylerWanna point it out for the rest of us?

    • @GumbootMan
      @GumbootMan Рік тому

      @@mindstyler I tried it and it threw a runtime exception (MissingFieldException).

    • @that_rendle
      @that_rendle Рік тому

      @@mindstyler I tried it out, there was no check, it just threw an error at runtime.

  • @tedchirvasiu
    @tedchirvasiu Рік тому +2

    Well, but this uses attributes. So the method name has to be hardcoded, right?

    • @SimpMcSimpy
      @SimpMcSimpy Рік тому

      Yes, but you have to do that with reflection approach as well. So no difference compared to the old way.

    • @tedchirvasiu
      @tedchirvasiu Рік тому

      @@SimpMcSimpy The GetMethod receives a string as a parameter which can be a variable. If you do something dynamic, you can pass a computed value there. On the attribute you can't as far as I know.

  • @xslashsdas
    @xslashsdas Рік тому +1

    I dunno, since I've never seen a case where I had to actually do this and not be okay with lower performance maybe my opinion is irrelevant. But, making it easier and more powerful is like removing the safety from a gun because it's quicker to shoot.

  • @SeanAlunni
    @SeanAlunni Рік тому

    How does it compare to expressions performance-wise?

  • @jcmorin2007
    @jcmorin2007 Рік тому

    I would like to point that in my code I'm using reflection for calling public type in some cases where I don't have instance of the class, but just the type. (static fields for instance)

  • @V123098456
    @V123098456 Рік тому +1

    Very interesting but something I would need a very good reason to use.

  • @BoleslawMalinowski-u2u
    @BoleslawMalinowski-u2u Рік тому

    Might the reason of introduction of such logic was better support of Roslin-like compilations, as in case of Reflection those examples just wouldn’t work?