如何在Visual Studio 2022中搭配.NET Standard 2.0或.NET Framework 4.8使用CallerArgumentExpression特性
CallerArgumentExpressionAttribute in .NET Framework 4.8 or .NET Standard 2.0? I'm familiar with using the CallerArgumentExpression attribute in Visual Studio 2022 with .NET 6.0 to capture the expression passed to a method, which helps generate friendlier error messages in diagnostic/test APIs and reduces code duplication. For example, I have this contract class for null checks:
public static class Contract { public static T RequiresArgNotNull<T>(T? item, [CallerArgumentExpression("item")] string? expression = default, string? message = null) where T : class { if (item == null) throw new ArgumentNullException( expression ?? "<unknown>", message ?? (expression != null ? "Requires " + expression + " != null" : "RequiresArgNotNull() failed.")); return item; } }
And I use it like this:
using static ClassLibrary1.Contract; static void test(string theString) { RequiresArgNotNull(theString); // No need to pass the parameter name explicitly Console.WriteLine(theString); }
If theString is null, it throws an exception with a clear message:
System.ArgumentNullException: Requires theString != null
Parameter name: theString
I want to use this same functionality in .NET Framework 4.8 and/or .NET Standard 2.0. Is this possible?
Answer
Great question! The short answer is yes, you can use a version of this functionality in .NET Framework 4.8 and .NET Standard 2.0, but with a few key caveats since CallerArgumentExpressionAttribute is a native .NET 6+ runtime feature. Here's how to make it work smoothly:
1. Define your own copy of the attribute
Since the official attribute doesn't exist in older frameworks, you'll need to create an identical-looking version in your project. The C# compiler only cares about the attribute's name and signature, so this will trick it into generating the argument expression just like in .NET 6. Add this code to your project:
namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public sealed class CallerArgumentExpressionAttribute : Attribute { public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } public string ParameterName { get; } } }
2. Ensure you're using a compatible compiler
This approach relies on C# 10 or later, which comes with Visual Studio 2022. Even though you're targeting .NET Framework 4.8/.NET Standard 2.0, the compiler version is what enables the expression injection. If you're stuck on an older Visual Studio version (pre-2022) that doesn't support C# 10, this won't work—you'll have to fall back to explicit nameof() calls for parameter names.
3. Adjust project settings if needed
Make sure your project is configured to use C# 10 or higher. In Visual Studio:
- Right-click your project → Properties
- Go to the Build tab → click Advanced
- Set the Language version to
C# 10orLatest minor version
4. Handle cross-framework compatibility (if needed)
If your library targets multiple frameworks (including .NET 6+), use preprocessor directives to avoid conflicting with the official attribute:
#if !NET6_0_OR_GREATER namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public sealed class CallerArgumentExpressionAttribute : Attribute { public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } public string ParameterName { get; } } } #endif
What to expect
Once set up, you can use your Contract class exactly as you did in .NET 6. When you call RequiresArgNotNull(theString), the compiler will automatically inject "theString" as the expression parameter, and you'll get the same clear exception message when the argument is null. The only runtime difference is that we're using a custom attribute instead of the official one—but since the attribute is only used at compile time, this won't cause any runtime issues.
内容的提问来源于stack exchange,提问作者Matthew Watson

