The New Way of Calling Your Code in .NET 8 Is INSANE
Вставка
- Опубліковано 28 вер 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
This is probably a crime in +50 countries.
It is also forbidden by Genebra convension!
It means we can do anything what we want to be in any framework we’re using😂
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
Mama i am a criminal...
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.
INSANE
Make sure your comments are totally off base too 😉
Truly a gAmE-cHaNgEr
WARNING *sarcasm* DETECTED
You could have done that in previous versions of .NET too. It's just become a more pleasant coding experience to do so now 😛.
I'm guessing this is part of getting rid of reflection for AOT...
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.
Interesting.. Is it because this can be done at compile time or something?
@@z0nx Exactly, I think they will combine this with source generator to generate code at compile time and not during runtime ;).
Yup Native AOT plays a big role in .Net 8
@@peanutcelerySomebody has been paying attention 🫡
With great power comes great responsibility.
Don't use your power to change static readonly fields :D
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!
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.
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.
Compile time performance is the best part of this ❤
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.
You can probably get the same perf with reflection if you compile lambda expressions
@@BillyBragayeah I think he did something like that in another video, not sure why didn't in this one
"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.
Who would win: ten years of SOLID principles vs one unsafe boi?
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
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.
@@leerothman2715 agreed I think we are all mostly on the same page of this can be useful but only in rare or extreme situations.
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.
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.
That will be very useful, especially on nuget packages those not maintained anymore.
@@phw1009how would you improve the packages that not maintained anymore? Do you mean changing inners of packages or extending functionality?
@@noon9548 It could be enough to be able to access some internals to be able to work around a problem.
The first place I will use it is to grab the array under List
You can already do that with CollectionsMarshal.AsSpan()
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.
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
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.
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
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.
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.
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
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.
Maybe it's just for use in highly optimized libraries
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.
@@SimpMcSimpyThe property name is still a string though, rather than a static reference... Will this help with AoT?
This will be really useful for mocking libraries and testing 😊
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.
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...
.NET developers before:
C++ is unsafe, so we invented delegates in C#
.NET developers now:
Now we can call functions in unsafe manner as C++
Great job!🤣🤣🤣
This is gentlemans, worst way to criticize
LOL. This is worse than any bad pattern in C++. It lets any class call and modify private methods and fields easily. The next time I see a C# developer ranting about the friend keyword in C++, I'm countering with this 🤣
C# has had unsafe keyword since the start, they avoid it by default to save devs from tedious operations and to be 'more safe', but it's always been there. C# is used in different applications than c++ making unmanaged code much less needed.
@@jean-michelgilbert8136
This is expected when you are redesigning JIT compiler to support AoT.
They run into wall with reflection and trimming.
Your observation is correct but I think you are drawing the wrong conclusion. Remember that the type safety was not put in place because type safety is a wonderful thing in and of itself. The main purpose behind it was the almighty dollar: type safety results in fewer bugs, so development goes faster and as you know time is money.
As said in the video MS made this feature because there are some solid use-cases for it. You could take the perspective that they are running back to C++, and in some sense that is exactly what they are doing. But the fact is that this is all part of an ongoing, more elaborate, evolution. In the same category you will find for example CYTHON (Python running back to C++).
I think we should get used to the arrival of new or renewed languages that will put the best of both worlds together. Transition will be like going from an automatic car to driving a stick. I think it will benefit all of us.
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.
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.
...and if we're lucky, they'll only charge developers $150 per .NET major version!
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.
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.
This is an amazing feature, only a shame that you haven't included a benchmark for a compiled lambda as well.
Its great till my colleagues don't know about it :D
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
I tried. You can't set its value since its marked as a JIT intrinsict
@@nickchapsas that's great
I got ill just by hearing your voice, get well soon Nick
Damn I didn't know people would notice! Thanks ❤
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.
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.
Sometimes you gotta do what you gotta do
This looks like one of those features where you may only need it once a year. But you'll *really* need it.
This is how I feel. If you have to use this against your own code, then your design is bad. This just looks like a hack for 3rd party libraries... which I'm not in favor of. I feel like if you use this regularly, then you're doing a lot of things wrong. In my 13 years of development, I could maybe point to 2 or 3 times where it would have been nice to access private members of a 3rd party class - but there was always some other solution. I'm kind of surprised Microsoft is releasing this.
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.
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.
Seems useful for unit testing your private methods.
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.
I see it very usefull for testing in some scenarios.
As a developer and an engineering manager, I can say that as helpful as this maybe it wreaks of code smell!
This is Assembly Publicizer which has been available for a long time in modding community
It's really helpful feature in some rare cases. Thanks for the sharing.
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?
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.
The fact that this is promoted as an improvement just shows that Microsoft is not understanding what other languages and editors are doing with meta programming and treesitter. While C# has things like "nameof" that might help avoid the use magic strings, they release this feature that further propagates their use.
Don Syme understood this with Type Providers. Ultimately, C# is becoming such an utter mess it's disappointing.
I am totally opposed to the thinking that "too much power" or "is too unsafe" in a language. C# suffered from this thinking for a very long time until recent years when they begin adding some more powerful features and better management over arbitrary memory. A "safe" language should have all the guardrails in place, but completely removing the ability for the programmer to explicitly bypass them can be frustrating where there is something non-standard that needs done.
Building and emitting IL at runtime was previously the next best thing to do other than just using a different language, so I am glad to see MS putting some more power and trust in programmers.
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).
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
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.
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.
Which editor is he using? Doesn't look like Visual Studio nor VS Code.
With great power comes great responsibility! 🕷
Oh man, once you said the word "extern," I knew I was in for a wild ride. But then I was like, "alright, well, good thing I try to make stuff read-only whenever I can," but then you edited the static read-only field. Well, RIP to that idea. This was all pretty crazy, but that put it over the top.
every time he said "the name is" I filled "Primogean" in my head. Please help
My prediction is that in a few years c# documentation will be bigger than the bible. The fields and classes will have some 15 keywords to define them. I'm now waiting for AI related keywords to brought into the language. 🎉🎉
Right there are so many methods and properties added every update. I came from Java and I’m like damn this language updates slow but then I came to C#…
@@peanutcelery c# is moving way too fast now I understand why Java is more popular, there's no way to keep up with C# and have a life at the same time
Yeah, it will be like:
[ChatGptImplemented]
public partial int GetWeatherForCity(string city){}
and will automatically fill the method body for you at build time. And will write a personalised resignation letter as well.
@@solovoypasando hell yaa.i moving to open source soon enought
Nick: *extern* you probably haven't seen this keyword before.
Me, who interops C/C++ to C# for a living: Yeah Nick, I wish. I really wish.
What happened ? Spell it out boy.
Just straight up breaking encapsulation and you are also proud of this "insane" feature. Bury this deep in the documentation and let only people know that actually know what they are doing. The rest can move on, there is nothing to see here.
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?
Could you give examples of when we MIGHT need to access a private method ?
Unit tests
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)
@@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.
@@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!)
Wonderful insights, thanks!
Thanks for the awesome demo, Nick!
I don't think it's something that I am going to use in the near future. Plus, I am not aware of scenarios that warrant messing with private members of classes in a C# app code.
Perhaps, other viewers would describe such scenarios?
It's mostly for Ahead of Time compilation.
Video game modding is also a good example (though modders DEFINITELY were not the target Microsoft was aiming at 😂)
Quite a few in frameworks/engines like Unity. E.g. editor extension and code reuse you otherwise would have to write yourself from scratch.
This is a great tool in combination with source generators
I see a problem with this, the fact that we can access unused private methods, while it is technically being used. It still says the private method is not used and it is safe to remove. Doing an auto cleanup or removing some unused code can be dangerous now no? Thoughts?
Does this work for with the BCL too? If so, then it makes all of the BCL virtual, seemingly.
1 year from now nick will go like "now Im become death, the destroyer of clean codes"
In C++ you can just do #define private public, infinite performance! :P
That violates the One Definition Rule though. A cleaner solution would be to add a #define HACK_ClassNameToUse ClassName inside the 3rd party header file. Put that inside an #ifndef HACK_ClassNameToUse . Then, rename the class to HACK_ClassNameToUse . Also make sure to change the private: keywords to a custom private_or_public: keyword that is defined to private when using the original name of the class and public otherwise. Afterwards, you can reinterpret_cast to the renamed class to access private fields and methods. Congratulations, you have broken encapsulation in C++ of a 3rd party lib without breaking the One Definition Rule. I'm insisting on the code being 3rd party because, if the code is 100% yours, you can just add the friend keyword to the class and be done with it.
My favorite was #define struct union ;) Did that to a buddy once in college about 30 years ago. We loved pulling those gags on each other. He got me back.
@@xlerb2286 damn, that was mean ;)
My favorite .NET guy. Always energy and excitement.
this is what you need for writing unit test when you don't want to expose everything as public
I can almost hear typing noises of dotnet tech leaders updating their teams' code guidelines in 'Forbidden techniques' section xD
It provides the same end result for the developer, but comes with a significant perf improvement; setting protected members using reflection has been a thing for years using reflection.
I actually think that UnsafeAccessor is safer than the previous example using reflection.
Using the attribute; the class name, method name and kind needs to be known at compile time.
Using reflection you can traverse and call unknown code, which I think is way more unsafe.
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.
Any idea if it works with Local functions, if so that would be so useful for unit testing
Microsoft: *pulls out hand grenade, removes safety pin and hands it to you* > here you go
Are they going to powercreep the private keyword, and give us a "CannotBeUnsafeAccessed" attribute in the future?
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?
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.
@@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...
@@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.
@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
@@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. 🤹
I would never be allowed to check something in that uses this outside of a debugging of library code we don't control. It would never be allowed into Production.
I don't know, most of the time you would use reflection when you don't know the member name at compile time, and this approach does not apply to such scenarios because it uses attributes. I agree there might be some use cases for it - e. g. getting more convinient and performant way to access private members, but it's so rarely needed that I wouldn't call this something super powerful.
The most important use case is testing. Now I have to turn my private method to internal, which is far worse for me.
Oh my goodness I do not like this at all. For most "normal" code I would absolutely refuse to merge something like this. It would have to be an extraordinary or niche situation, and I would want to make sure there were plenty of comments and lots of documentation around it to make sure everybody knew what was going on. Visibility modifiers and readonly tags have meaning, and going around those things violates the Principle of Least Surprise in a stunning way (not to mention basic rules of encapsulation, etc).
"Hello everybody i'm naked in this video", subtitles are getting really interesting here
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.
I think it is useful to test private methods, but i think it is a bad practice in non-testing code because if you have something private shouldnt be called from others classes and if you need to do it then you have an issue in your code and you should fix it
I only access private members when needed for Unit Testing, and therefore I would prefer a way to tell any assembly that another one is for testing and let it have plenty access to its private members instead of all this boilerplate.
Thanks, Its a good one, But I have a small question,
you are calling the private method outside that class.
Q1: But why?
Q2: you are calling that private method inside a public class and public method, instead can't you make the private method public?
Nice feature, but in term of software quality, i am not sure if general developers should use it. Especially, if we are working in a team and your code is hard to read and maintain by others
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.
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?
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.
For sure I'm gonna use that! Imagine this while generating proxies for decorators, ORMs, etc! It might allow the tool to be not intrusive but with all expected behavior and almost no performance hit!
Your non-cached reflection method uses GetType(), while your cached reflection method uses typeof(). The latter should perform better. I'm surprised you didn't do the same in both, for consistency. That said, I guess the end results wouldn't be that different.
Great for testing. No hit in runtime, production code doesn't need to change to accommodate testing infrastructure.
can you get static field of Method() and replace it with new Func that has completely different implementation?
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?
wow this is dangerous, but also cool!
Could it be that Microsoft has added this to gain extra nanoseconds in EF core? To really try to win the speed race with dapper? As much as I understand EF core uses reflection under the hood.
It's great and your videos are great.
The idea behind "private" is to not interfere from the outside. It's considered bad code, and if you believe you need to access such a member property, method, or field, you're likely making a mistake. Big time!
How abouth where there is more than one method with the same name (overloaded)?
Auto-reject if I see this in a PR
I really want to buy your courses since they look really cool, but as a South African, it's just too expensive because our currency is so weak; could you supply South Africa discount codes?
What about generic methods? Calling them was the most painful for me?
The only allow use case i see is to easy testing, through unit test, more deep code part without changing the protection to internal.
So if we use a 3rd party component, with a license check inside, and its result is cached in a private field - we can set it as we like ? To demotivate developers to sell solutions ?
I want to call private methods in unit tests in php. It should be possible, no discussion!
Do we really need this practically?
You totally mixed up 'readonly' with true consts. 'readonly' exists only in C# language, it is NOT handled by CLR. Contrary to what you said, reflection in C# has not a slightest problem with modifying readonly fields, contrary to what you say at 12:04