【问题标题】:How could an extension method be attached to a generic class when the type argument is IEnumerable<T>?当类型参数是 IEnumerable<T> 时,如何将扩展方法附加到泛型类?
【发布时间】:2013-03-03 07:33:03
【问题描述】:

作为一个爱好项目(为了让自己更深入地沉浸在泛型/扩展方法中),我正在编写一个参数检查库!

我有一个名为 Argument 的模型,它描述了一个参数,看起来像这样:

public class Argument<T>
{
    internal Argument(string name, T value)
    {
        Name = name;
        Value = value;
    }

    public string Name { get; private set; }

    public T Value { get; private set; }
}

当开始对参数进行验证时,会创建此对象的一个​​实例,并通过调用与其相关的扩展方法(包含实际逻辑)来执行单独的验证。

一个这样的扩展方法验证一个集合至少包含一个项目,目前看起来像这样:

public static Argument<IEnumerable<T>> HasItems<T>(this Argument<IEnumerable<T>> argument)
{
    if (!argument.Value.Any())
        throw Error.Generic(argument.Name, "Collection contains no items.");

    return argument;
}

但它似乎不起作用。如果我要编写这个单元测试:

[TestMethod]
public void TestMethod1()
{
    var argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 };

    Validate.Argument("argument", argument)
        .IsNotNull()
        .HasItems()
        .All(v => v.IsGreaterThan(0));
}

HasItems 没有出现在 Intellisense 中,并且我得到这个编译错误:

'Validation.Argument&lt;System.Collections.Generic.List&lt;int&gt;&gt;' 不包含“HasItems”的定义,并且找不到接受'Validation.Argument&lt;System.Collections.Generic.List&lt;int&gt;&gt;' 类型的第一个参数的扩展方法“HasItems”(您是否缺少 using 指令或程序集引用?)

如果我尝试将值直接传递给扩展方法,如下所示:

CollectionTypeExtensions.HasItems(Validate.Argument("argument", argument));

我明白了:

'Validation.CollectionTypeExtensions.HasItems&lt;int&gt;(Validation.Argument&lt;System.Collections.Generic.IEnumerable&lt;int&gt;&gt;)' 的最佳重载方法匹配有一些无效参数

根据我的研究,我需要的是“变体”,它适用于接口和委托,但不适用于类(即:所有类都是不变的。)

也就是说,它可能会以另一种方式工作。想到的一个是将其重写为直接进入 T,如下所示:

public static Argument<T> HasItems<T, TElement>(this Argument<T> argument)
        where T : IEnumerable<TElement>
{
    if (!argument.Value.Any())
        throw Error.Generic(argument.Name, "Collection contains no items.");

    return argument;
}

..但这也不起作用,因为它需要在调用方法时显式指定 TElement 。我也可以回退到在类型约束中使用非通用 IEnumerable 接口,但是我必须找到一种方法将 IEnumerable 强制转换为 IEnumerable (这需要知道 T 在该上下文中是什么),或者复制Any() 的功能是为了测试是否存在任何项目,并且还有另一种扩展方法(All),这将非常非常混乱,所以我宁愿避免它。

所以最终,我想我的问题是:如何让我的扩展方法正确附加?

【问题讨论】:

    标签: c# .net generics compiler-errors extension-methods


    【解决方案1】:

    这对你有用吗?看起来有点笨拙,但它确实有效。

    public static Argument<T> HasItems<T>(this Argument<T> argument) where T: IEnumerable
    {
        if (!argument.Value.Cast<object>().Any())
        {
            throw Error.Generic(argument.Name, "Collection contains no items.");
        }
    
        return argument;
    }
    

    【讨论】:

    • 那...确实有效!今天早上当我用新的眼光看待这个问题时,我最终选择了一条不同的路线,但我将其标记为答案,因为它简洁地解决了所提出的问题。谢谢!
    【解决方案2】:

    我相信你真正想要的是一个接口IArgument,它在T上是协变的:

    public static class Validate
    {
        public static IArgument<T> Argument<T>(string name, T value)
        {
            return new Argument<T>(name, value);
        }
    }
    
    public interface IArgument<out T>
    {
        string Name { get; }
        T Value { get; }
    }
    
    public class Argument<T> : IArgument<T>
    {
        internal Argument(string name, T value)
        {
            Name = name;
            Value = value;
        }
    
        public string Name { get; private set; }
    
        public T Value { get; private set; }
    }
    
    public static class ExtensionMethods
    {
        public static IArgument<T> IsNotNull<T>(this IArgument<T> argument)
        {
            return argument;
        }
    
        public static IArgument<IEnumerable<T>> HasItems<T>(this IArgument<IEnumerable<T>> argument)
        {
            return argument;
        }
    
        public static IArgument<IEnumerable<T>> All<T>(this IArgument<IEnumerable<T>> argument, Predicate<T> predicate)
        {
            return argument;
        }
    }
    
    [TestMethod]
    public void TestMethod1()
    {
        List<int> argument = new List<int>() { 1, 2, 6, 3, -1, 5, 0 };
    
        Validate.Argument("argument", argument)
            .IsNotNull()
            .HasItems()
            .All(v => v > 0);
    }
    

    【讨论】:

      猜你喜欢
      • 2023-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多