If you are using Visual Studio 2022, you can use CallerArgumentExpression with .net 4.8 and/or .net Standard 2.0 by defining a local internal implementation of CallerArgumentExpressionAttribute like so:
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
public string ParameterName { get; }
}
}
Note that you must use the System.Runtime.CompilerServices namespace in order for this to work correctly. By making this implementation internal you can guarantee that it won't conflict with any system-defined implementation.
This will compile as a .net Standard 2.0 target, so it can be also used by assemblies that target .net 4.8, .net Core 3.1 and so on.
Also note that you still require Visual Studio 2022 and its SDK for this to work - if you try to use Visual Studio 2019 it will compile OK but the argument will be null.
You can include the above class in the assembly that uses CallerArgumentExpression and it will work as expected.
Sample .net Standard 2.0 class library providing Contract.RequiresArgNotNull():
(This sample assembly is using namespace ClassLibrary1 for brevity.)
Project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8</LangVersion>
</PropertyGroup>
</Project>
CallerArgumentExpression.cs:
namespace System.Runtime.CompilerServices
{
#if !NET6_0_OR_GREATER
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
public string ParameterName { get; }
}
#endif
}
Contract.cs:
using System;
using System.Runtime.CompilerServices;
#nullable enable
namespace ClassLibrary1
{
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;
}
}
}
Sample console application demonstrating the use of RequiresArgNotNull():
using System;
using static ClassLibrary1.Contract;
#nullable enable
namespace Demo
{
class Program
{
static void Main()
{
try
{
test(null!);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void test(string theString)
{
RequiresArgNotNull(theString);
Console.WriteLine(theString);
}
}
}
This will output the following exception message (for my particular build):
System.ArgumentNullException: Requires theString != null
Parameter name: theString
at ClassLibrary1.Contract.RequiresArgNotNull[T](T item, String expression, String message) in E:\Test\cs9\ConsoleApp1\ClassLibrary1\Contract.cs:line 18
at Demo.Program.test(String theString) in E:\Test\cs9\ConsoleApp1\ConsoleApp1\Program.cs:line 25
at Demo.Program.Main() in E:\Test\cs9\ConsoleApp1\ConsoleApp1\Program.cs:line 14