The C# Feature I Use Instead of Reflection
HTML-код
- Опубликовано: 16 сен 2024
- Check out the ABP Framework here: bit.ly/3UmfRbh
Check out my courses: 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 a set of attributes that allow you to get compile-time metadata about your code and use them in runtime. In many cases the data you will collect are impossible to get with something like reflection during runtime.
This video is sponsored by abp.io.
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
I think it was mentioned in a recent stream with Mads Torgerson that there is a proposal to add a [CallerMemberInfo] attribute which would work the same as [CallerMemberName], but would allow you to recieve a MemberInfo instead of a string. Personally I think that would make the entire "caller" family complete, and would also act as a more reasonable alternative to the proposed infoof expression.
That would be a great addition.
I've used all except CallerArgumentExpression. One of the more useful things I did was inject the Caller, File, and Line number into the logger scope at the start of methods, making debugging from logs much easier, especially if you're looking at logs that don't have stack trace.
hi! do you have a sample on how to do that? was looking for it myself :-)
@@jptouron3080
public static class LoggingExtensions
{
public static IDisposable? AddCallerScope(this ILogger logger, string memberName = "", string filePath = "",
long lineNumber = 0, [CallerMemberName] string methodName = "", [CallerLineNumber] long logScopeLineNumber = 0)
{
return logger.BeginScope("{callerMember}, {callerPath}:{callerLine} -> {method}:{lineNumber}",
memberName, filePath, lineNumber, methodName, logScopeLineNumber);
}
}
then simply use like this:
public void SomeMethod(int methodArgument, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] long lineNumber = 0)
{
using var logScope = _logger.AddCallerScope(memberName, filePath, lineNumber);
.........
CallerMemberName is useful if you are making an INotifyPropertyChanged implementation that handles multiple properties. You can have your property setters call a private helper method that has a CallerMemberName and use this to access the property name that was modified easily, so you can add it to the EventArgs.
Why not simply use an MVVM framework, caliburn prism or whatever and the PropertyChanged nuget. Completely remove them from code and implement them via some form of precompilation on classes that implement INotifyPropertyChanged. INotifyPropertyChanged and much of the WPFish stuff is just boilerplate you don't want to deal with. Use auto properties and if you need to do something override the created methods.
I love the caller attributes, i use them whenever i can. But argument expressions was new for me, so thanks for sharing.
Merry Christmas, Nick! You’re doing awesome work with this channel!
One of the important things is getting the type of the returned value of the expression
6:16 I was going to call you out on the use of nameof for number, but of course you had it!
I can see a good use for the last two. Sometimes you cannot attach a debugger (maybe it's running at a customer's location) and to debug where something is happening, you sprinkle a lot of logging in the code. This would allow you to write a static LogDebug message that automatically knows the filename and line of where it's called and include that in the log message.
Thank you for the video. It was very helpful. These features look really handy if you're implementing deeper diagnostic logging for your code. For example, I needed to do this for a shared library I was working on. I needed to log information about what modules and classes called it, as well as the arguments it was called with, so that I could log parameters and results and have an idea about how people were using the library. This way, we could have a statistics report at the end of the day. I had to use a lot of reflection and do a lot of string parsing of the Current StackTrace, but it still wasn't enough. I had to add extra parameters to my methods so developers could pass more information. If these features were available back then, it would have been a lot of use to me.
CallerMemberName is used extensively in WPF
Fits perfectly with INotifyProperyChanged.
Great video. I’ve been a fan of the caller member attributes for enhancing error log details for some time. I didn’t know about that feature to capture the input arguments. Very cool and thank you for sharing.
We usually like to enrich logging with custom fields and they way we are doing it with ILogger - I believe these attributes are not as useful because there will be a number of methods in stack trace between ilogger call and actual logging handler
Definitely gonna implement the line number caller in the exception libraries
Very fascinating stuff! Question: is there an attribute that can get all of the caller values for all of the arguments when there is more than one without having to individually add a argument for each new one?
If you will call Example() methods from several another methods, each compilation result will call Example() with it name.
Test1() => Example("Test1")
Test2() => Example("Test2")
This is last example is actually similar to the C++ macro __FILE__ and __LINE__, which can be useful in UTests.
I use CallerMemberName, CallerFilePath and CallerLineNumber in a generic logging helper class that I reuse in projects which basically just contains wrappers for the Serilog Information, Warn, Error etc functions, which prepends the file/member/line number at the beginning of the message, so I can see where in the code the log was created from. For the file path, I'm using Path.GetFileNameWithoutExtension() so as to just keep the filename so logs arent cluttered with the full path. I did not know about the new Argument one, it's interesting but not sure when I'd use it.
These attributes are a great addition but still very situational. For example, they can not work with params constructs, and this really breaks their usage in most logging methods, unless you are using string interpolation in your message.
Exactly, which is the problem I'm now running into when setting up Serilog the correct way
Is there an c# IL viewer extension for visual studio(not code)? 😤
Have a look at "IL Spy 2022"
And check ILDasm.
I’m curious what happens if you mix and match languages with the argument one - like you call from VB, passing in a lambda to a C# method. I guess that’s something to try later.
Wonderful, it's more on build time rather than compile time.
Hi,great video. can you do a session on model validation performance in webapi world? Always curious if fluent validation is faster or slower than the default validation attributes?
Would be interesting to get the CallerArgumentExpression as an actual Expression
Those could be extremely useful for debugging dlls created at runtime.
Thanks
I use CallerMemberName the most, its really handy for implementing IPropertyChanged.
CallerFileName and line number can be really useful for error logging.
Wish the added new keyword pathof(exp) which would do the same job as nameof but returning full path eg. Foo.Bar.PropName instead of just PropName
Hello. Could anybody help me - what is extention are used on 7:10 as IL viewer but with higth level c#?
this works in unity?
Hi nick thanks a lot for yours courses and videos ... Can you please make a course for mongoDB and dynamic DTO to validate dynamic forms or dynamic JSON
Can you get the class name?
AN’T WORK?
I feel like this feature could be used to exploit vulnerabilities in calling code when implemented in a library. The library uses this feature to find the names of methods in code that calls it, and uses this to seek out exploits by brute force calling those methods using reflection...
Oh wow, the expression one is even worse. A library could extract all sorts of sensitive data that whoever wrote the calling code thought the library wouldn't have access to because it's being evaluated before being passed in! Yikes!
e.g. CallLibrary(HashPassword(pword, "secretsalt")); exposes the salt
@@EdKolis putting your salt in... right as a string there... anyone could get that out of your source with reflector, dotPeek, ilDasm. And really... that goes for all of your code. It really is a non-issue at that point.
Unless you put secrets in your version control, This isn't an issue at all.
Installing a malicious nuget package can already do much worse things than getting some potentially hardcoded string.
Like hell, even the dumbest thing - it could literally scan the entire executable on runtime and send all the strings it finds to the attacker.
If you're worried about it the actual issue is that you're using a fishy library in the first place, not the feature.
It's kind of like worrying that your bathroom lock is too weak to stop a thief. Worry about your front door first.
First
First