如何编写泛型扩展方法来转换 C# 中的类型

本文关键字:类型 转换 何编写 泛型 扩展 方法 | 更新日期: 2023-09-27 18:30:52

我正在编写一个静态保护类/api来验证发送到方法的参数。

到目前为止的代码如下所示:

public static class Guard
{
    public static GuardArgument<T> Ensure<T>(T value, string argumentName)
    {
        return new GuardArgument<T>(value, argumentName);
    }
    public static T Value<T>(this GuardArgument<T> guardArgument)
    {
        return guardArgument.Value;
    }
    // Example extension method
    public static GuardArgument<T> IsNotNull<T>(this GuardArgument<T> guardArgument, string errorMessage)
    {
        if (guardArgument.Value == null)
        {
            throw new ArgumentNullException(guardArgument.Name, errorMessage);
        }
        return guardArgument;
    }
}

它可以这样使用:

public void Test(IFoo foo) {
     Guard.Ensure(foo, "foo").IsNotNull();
}

现在的情况要求我需要从提供的接口转换为具体类型。不要问为什么,我只需要!

我想添加一个As扩展方法来GuardArgument执行此操作,如下所示:

public static GuardArgument<TOut> As<TOut, TIn>(this GuardArgument<TIn> guardArgument, Type type)
        where TOut : class
    {
        // Check cast is OK, otherwise throw exception
        return new GuardArgument<TOut>(guardArgument.Value as TOut, guardArgument.Name);
    }

不过我不太喜欢语法。我希望能够按如下方式使用该类:

 Foo foo = Guard.Ensure(foo, "foo")
             .As(typeof(Foo))
             .IsNotNull()
             .Value();

不过,我不确定如何编写扩展方法来允许这种语法。 我意识到我可以使用现有的流畅 API 作为:

 Foo foo = Guard.Ensure(foo as Foo, "foo")
             .IsNotNull()
             .Value();

但从可读性的角度来看,我不喜欢这样。

如何编写泛型扩展方法来转换 C# 中的类型

你可以得到这个语法:

Foo foo = Guard.Ensure(foo, "foo")
          .As<Foo>()
          .IsNotNull()
          .Value();

诀窍是放弃TIn类型参数。它不用于 As() 方法,并且当由于TOut而无法使用类型推断时,会使 API 膨胀。为了能够在不对所有类型都As()建议的情况下做到这一点,您必须为 GuardArgument<> 类实现一个新的非泛型接口:

interface IGuardArgument 
{ 
  object Value { get; }
  strign Name { get; }
}
public class GuardArgument<T> : IGuardArgument
{
  // Explicit implementation to hide this property from
  // intellisense.
  object IGuardArgument.Value { get { return Value; } 
  // Rest of class here, including public properties Value and Name.
}

现在,您可以仅使用一个泛型参数编写 As() 方法:

public static GuardArgument<TOut> As<TOut>(this IGuardArgument guardArgument)
    where TOut : class
{
    // Check cast is OK, otherwise throw exception
    return new GuardArgument<TOut>(guardArgument.Value as TOut, guardArgument.Name);
}

引入 GuardArgument{T} 实现的 IGuardArgument 接口。然后,可以从 As 扩展方法中删除 TIn,并删除 Type 参数。签名:

public static GuardArgument<TOut> As(this IGuardArgument guardArgument);

用法:

Guard.Ensure(foo, "foo").As<Foo>().IsNotNull()