THIS Is Even More "Unsafe" Than Reflection In DotNet!
September 11, 2024
• 801 views
Everyone will warn you about using reflection in DotNet.
And, generally, this is for a handful of good reasons:
- It's slower than typical non-reflection code
- It's usually "breaking rules" that we have
- You might not even need it!
In this video, I'll explain Unsafe Accessors in CSharp and how they can do some powerful reflection-like behavior.
View Transcript
reflection has a bad reputation for a handful of different reasons but what if I told you there was a new kid on the Block that could also do some pretty crazy things hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in this video we're going to look at unsafe accessors and how they can compare to reflection now we're going to be walking through an example where we can start modifying and leveraging private members of a type which we're not usually able to do unless we use reflection however with unsafe accessors we can do the same type of thing which is very interesting the syntax is a little bit more concise and if you stick right to the end of this video you'll see some interesting performance characteristics if that sounds interesting remember to subscribe to the channel and check
out that pin comment for my courses on dome train with that said let's jump over to visual studio and start walking through this example all right on my screen right now I have a bunch of reflection code but I want to start by going through the type that we have a little bit down below so I have this public class hour type you can see on line 72 and then all of the members that we have inside of here are all marked as private or private static and that includes the Constructor so technically if we wanted to do really anything useful with this class we kind of have to use reflection to do anything this is just a contrived example I'm not suggesting that you go write code like this by any means I don't want you to take that as the the lesson from
this uh certainly if you needed to be able to call these methods you would in fact try to make make them public or internal so that you could access them this is purely for the example of being able to set up a reflection scenario and then for unsafe accessor so that's a total disclaimer I don't want to see any comments about how this code is crappy because it's all private and you could change it cuz that's obviously the case everything we see here is going to be marked as private like I said including the Constructor what I want to be able to illustrate as well is that we have uh private fields and when we go to call these methods they will also print out the private field value so that'll be helpful later when we go to call these things and we want to
prove that they're being executed we'll be able to see this information go to the console so from line four down to 22 is a pretty typical reflection code that we could go leverage to be able to go access the private fields and to call the private methods things like that so I'm going to walk through what this does and then we're going to see the variation of this with unsafe accessors so on line 4 through 9 is where I'm going to create the instance of the type I am using activator create instance you can use Constructor info or type invoke member to do a similar type of thing but we need to make sure that when we're using reflection and we're dealing with private that we have to have non-public binding Flags or else the reflection will not be able to sort of see the
thing that we're trying to access and for Constructors because it's a non-static Constructor I need an instance and I need create instance as well with the binding Flags then we just combine them them all with an ore this next parameter is not used and then 42 is going to be an array of objects and 42 is the parameter that I'm passing in because we only have one parameter on our Constructor again from line 78 to 81 is our Constructor here on 78 we see this single integer being passed in so scrolling back up this 42 is the single parameter being passed in and again we're not using the last parameter here so after line 4 through 9 execute we will have an instance of our type which is awesome cuz now we can start to do some things with it we are going to ask
for the private Fields value so again using reflection line 12 specifically is going to be getting that field info recall we have to use nonpublic you'll see that as I highlight non-public Visual Studio is pointing out all the other spots that's because everything we have on this type is private so we will need that and in this reflection example I am only using the members so you'll notice that I also have binding flags for instance across all of these we can get the value once we have the field info and we have to pass in the instance that we're dealing with and then the next part here I'm going to be setting that private field so I'm setting it to 1337 and just to kind of think through how this object is changing as we run this code I create it with a value of
42 I'm going to get that 42 back out we'll print it and see it then I'm going to set that value to 1337 and that means when we go to line 20 through 22 here when I get the method private method that will get us a method info when we call it we should be able to see that we print out one 337 that's because if I scroll down here line 88 through 91 is going to print out instance private and then the value of private field so hopefully that all makes sense let's go give this a run and make sure that we see that output before we continue on to the unsafe accessors before moving on this is just a quick reminder that I do have courses available on dome train if you're interested in learning more about reflection I've put together an entire
reflection course it's just over 5 hours long that walks you through all of the basics about reflection as well as some more complicated scenarios if we have a quick look at the curriculum you can see that we start diving into what types are in programming languages how we can find types how we can work with different member information on those types and that includes some fancy things like being able to set private readon fields which is pretty wild I Do cover more advanced topics like generics performance analysis and some things that are Beyond reflection remember to check it out on D train thanks and back to the video there we go as we see in the output here private field is 42 and that's coming from line 14 and again that's because we initialized our value to 42 when we called the Constructor and that
was through activator create instance then we end up seeing instance private 1337 and that's because we were able to update that private field with reflection and then when we called private method so line 22 where we're invoking it that's where we end up getting that console right line where it's printing out 1337 so that all worked with reflection but you already knew how reflection work right you want to see the unsafe accessors so let's go check out what those look like on my screen right now I have a bunch of unsafe accessors printed out here this is going to be the syntax we'll walk through it together but essentially you need an attribute that is called unsafe accessor that attribute has an unsafe accessor kind it's kind of like uh binding Flags like almost but not quite the same so we need to indicate the
type of thing that we want to be dealing with if we have a look through the different member types here we can see a Constructor and then we can see fields and methods but you'll also notice there's a static flavor of those two things as well we don't have events we don't have properties here but if you're familiar with reflection and typing and how properties and stuff work you might already know that properties in CP and.net they will essentially be just methods under the hood there's nothing special about properties except for the fact that they get this consistent naming convention with get uncore the property name and set uncore the property name those just become methods in in the end if you want to work with properties which we'll see in a little bit this is where we're going to be using method or static
method depending on if it's a instance property or static property so the Constructor let me put this back the Constructor here you can see on line 44 we need to have exter static and then our type is the return type when you call the Constructor and then we need to have the same arguments that we would have on our normal Constructor so in this case it is just a single integer parameter being passed in then I give it the name private C but these names of the methods can be whatever you would like them to be next if we go look at the fields you can see that unsafe accessor kind field now we're going to start to include a name of the field that we're interested in and this just literally matches the name that we have in code you'll also notice that we
have extern static here we have int and then get private field that's just the name we're going to have and we need to be able to pass in the instance of our type now if we go a little bit lower you can see that I have the same thing for the static private field but I want to call out that I have a ref keyword here and if I jump down to the bottom of my screen you'll also see from line 67 to 69 I've done the same thing so this one kind of looks like the private field one up here but this one has a ref keyword so both this one and on line 52 we see the ref keyword one's for the static private field the ones for the private field we're going to see how this ref gets used because I think
it's very interesting it seems pretty power powerful we'll check that out in just a moment we have the property as well so recall that I said when we're dealing with properties we need to have a get or set underscore you'll also notice that the unsafe accessor kind is marked as method and that's because properties are just methods they do have a syntactic sugar to make it look a particular way but truly this is what they get maap to and then finally these ones might seem obvious by this point but they are marked as method and static method because they are a method and static method as you might have guessed again just important to note that we do need to pass in our type instance here so we need to have for the non-static ones we need to pass in the instance but for the
static ones we still need this type here you'll see when we go to call this that we will be passing in null but we do need to have a type and a basically a parameter for all of them regardless of it being St static or not it's just that we don't pass in an instance on the static ones when we call it so let's go check out the code for how the unsafe accessors actually work when we call them okay so on my screen now I have commented out the reflection code but I do want to highlight all of the code at the bottom of the screen which is going to be using unsafe accessors so you might notice that it's a little bit more concise we don't have to do all the binding flags and things like that so we are doing a couple
of different things here but it's still similar code so to start start things off line 24 I am creating an incidence so we call Private C again you can name this method whatever you want and we pass in 42 but this means after line 24 executes we have used an unsafe accessor to create an instance of the type line 24 is essentially the equivalent to line 4 through 9 that's pretty interesting it's uh in my opinion it's a lot more readable if you need to be doing this kind of thing so that's awesome if we go down to line 26 through 27 we're going to be getting the value from the private property off of the instance so you'll notice I'm passing in the instance here we get that value we just write it out to the console and then line 29 is something that
we haven't seen quite yet but this is where that ref keyword is coming back we can see that I have a ref int reference to private field so that's the name of the variable marked as ref and then equals ref keyword again get set private field again passing in the instance so this might not be a familiar type of keyword that people uh use on their regular programming so not to worry we will see how this is going to have an effect for us in just a moment I do want to show that line 31 and 32 is the exact same thing for the static private field so we're just going to print the static private one out and then we're going to call both of those methods so private method so line 34 where we pass in the instance that is essentially the equivalent
to line 20 through 22 function fun Al the same idea here we're just repeating the same thing with the static private method and you'll notice that on both line 35 for the static private method I pass in aull and line 31 on the Get Set static private field I also pass in a null recall we needed to declare these things with the type but what we call them we're passing in null because they are static static things do not work on instances they work on the type the last little bit here I think is pretty cool so we'll see how this works but line 37 this reference to private field I did name it as such because it is marked as ref but we do have a truly a reference to the private field so we can go set this variable to 1337 and when
we call Private method we will see that we've updated the private field inside of the instance because it will print out 1337 we're not setting 1337 anywhere else with that said let's go run this code and see the output okay starting things off private property printing out 42 that comes from line 27 so when we created this thing we passed in a 42 getting the private property if I scroll down here private property is right here the bottom line 94 you can see that it's accessing the private field so it's just getting for us 42 off of private field then we print it to the console pretty simple on that one static private field we did initi iiz the static private field to 1 2 3 4 5 6 on line 73 here so if I scroll all the way back up sorry for jumping
around when we go to get that on line 31 and print it on line 32 we're just printing out that value as it was initialized next up on line 34 and 35 we called both of those methods so inside of them we print out instance private 42 and then static private again just printing out that static private field and that's why you see basically these numbers repeated here so just a heads up instance private 42 and static private 1 2 3 4 5 6 come from calling those methods the really interesting one in my opinion my favorite part of all of this is that reference to private field on line 37 we s to 1337 that way when we call the private method again it prints out 1337 instead of 42 so that should hopefully illustrate to you that we were able to update that
Fields value with the unsafe accessor now unsafe accessors are not a complete replacement for reflection so I do want to call that out there are differences so reflection we can dynamically load types at runtime start looking things up by their name and working them with them that way you'll notice that if we think about how these unsafe accessors are defined I need to know this type information at compile time so that is an important difference that I want to call out so if you're looking at this and going oh man I can't wait to replace all of my ref reflection code with this stuff you can't quite do that depending on what your reflection code is doing there are things like Source generators that you could use to maybe help with some other reflection scenarios but when we're dealing with unsafe accessors again you need
to make sure that you have this type information at compile time otherwise we wouldn't be able to even write the signatures for these things so it kind of falls apart if you don't have that information at compile time now I did mention that if you stayed right to the end of this video we could look at some performance benchmarks so when that video is ready you can check it out right here thanks and I'll see you next time
Frequently Asked Questions
What are unsafe accessors and how do they compare to reflection?
Unsafe accessors allow you to access and modify private members of a type in a more concise way than reflection. While reflection has a reputation for being complex and potentially unsafe, unsafe accessors provide a simpler syntax and can achieve similar results without the overhead of reflection.
Can I use unsafe accessors to access properties or only fields and methods?
You can use unsafe accessors to access both fields and methods, but properties are treated as methods under the hood. Therefore, when dealing with properties, you'll need to use the appropriate method signatures that correspond to the property names.
Are unsafe accessors a complete replacement for reflection?
No, unsafe accessors are not a complete replacement for reflection. While they offer a more straightforward way to access private members, they require type information at compile time, whereas reflection allows for dynamic type loading at runtime.
These FAQs were generated by AI from the video transcript.