如何在Visual Studio 2022和.net Standard 2.0或.net 4.8中使用CallerArg



使用Visual Studio 2022和.net 6.0,我们有了新的CallerArgumentExpression属性,可以用于"捕获传递给方法的表达式以在诊断/测试API中实现更好的错误消息并减少击键">

例如,我们可以编写一个类来检查空方法参数,如下所示:

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;
}
}

可以这样使用:

using static ClassLibrary1.Contract; // To allow just putting RequiresArgNotNull()
...
static void test(string theString)
{
RequiresArgNotNull(theString); // Note that we do NOT need to pass the parameter
// name as a separate string.
Console.WriteLine(theString);
}

如果theString为null,将抛出一个异常,如下所示:

System.ArgumentNullException: Requires theString != null
Parameter name: theString

我希望能够将此功能与.net 4.8和/或.net Standard 2.0一起使用。这可能吗

如果您使用的是Visual Studio 2022,您可以通过定义CallerArgumentExpressionAttribute的本地internal实现来将CallerArgumentExpression与.net 4.8和/或.net Standard 2.0一起使用,如下所示:

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; }
}
}

请注意,必须使用System.Runtime.CompilerServices命名空间才能正常工作。通过使这个实现internal,您可以保证它不会与任何系统定义的实现冲突。

这将编译为.net Standard 2.0目标,因此它也可以由以.net 4.8.net Core 3.1等为目标的程序集使用

还要注意的是,您仍然需要Visual Studio 2022及其SDK才能正常工作——如果您尝试使用Visual Studio 2019,它会编译正常,但参数将为null。

您可以在使用CallerArgumentExpression的程序集中包含上述类,它将按预期工作。


提供Contract.RequiresArgNotNull():的Sample.net Standard 2.0类库

(为了简洁起见,此示例程序集使用名称空间ClassLibrary1。(

项目:

<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
}

合同.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;
}
}
}

演示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);
}
}
}

这将输出以下异常消息(对于我的特定构建(:

System.ArgumentNullException: Requires theString != null
Parameter name: theString
at ClassLibrary1.Contract.RequiresArgNotNull[T](T item, String expression, String message) in E:Testcs9ConsoleApp1ClassLibrary1Contract.cs:line 18
at Demo.Program.test(String theString) in E:Testcs9ConsoleApp1ConsoleApp1Program.cs:line 25
at Demo.Program.Main() in E:Testcs9ConsoleApp1ConsoleApp1Program.cs:line 14

最新更新