限制 IList<T> 扩展方法以排除数组

Restrict an IList<T> extension method to exclude Arrays

假设我为 IList 创建了一个扩展方法,但这个扩展是一个可能在许多项目中使用的库的一部分。我无法控制它的调用方式。

有没有办法阻止 Array 在编译时调用 IList<T> 扩展方法?这是为了避免任何误用,如果将调用 .Add() 方法或仅调用索引器,则调用者无法猜测确切的实现。 我找不到具有通用约束类型的可能解决方案。

到目前为止,剩下的唯一可能就是将扩展方法直接限制为 List<T>

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var array = new[]{"Hello"};
        array.DummyInsert("World"); // this will crash at run time
    }
}

public static class DummyExtension
{
    public static T DummyInsert<T>(this IList<T> list, T insertValue)
    {
        list.Add(insertValue);
        return insertValue;
    }
}

我同意 Ed Plunkett 的观点,使用 ReadOnlyCollection<T>。但是你可以这样做。是你的脚,你想拍就拍。

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var array = new[]{"Hello"};
        var world = array.Insert("World"); // this will crash at run time
        
        Console.WriteLine(array.Length);
    }
}

public static class DummyExtension
{
    public static T Insert<T>(this IList<T> list, T insertValue)
    {
        Console.WriteLine("WrongInsert");
        list.Add(insertValue);
        return insertValue;
    }
    
    [Obsolete("If want a compile time exception you can do this too.", true)]
    public static T Insert<T>(this T[] list, T insertValue)
    {
        Console.WriteLine("RightInsert");
        return insertValue;
    }
}

这会打印

RightInsert

1

https://dotnetfiddle.net/i6p1Z5

编辑:

有人在下面的评论中指出,如果您的数组已显式或隐式转换为 IList<T>,这将不起作用。在这里使用 List<T> 而不是 IList<T> 没有任何问题,除非你试图实际扩展 IList<T>。在那种情况下,以一种对所有人都有意义的方式扩展它 IList<T>。我只是想表明,是的,您可以完成您的要求。能力越大,责任越大。

运行-time 问题是因为 Array 是固定长度的,因此当您尝试向其中插入一个元素时,您最终会遇到异常。相反,您可以为 case Array 使用自己的扩展方法并相应地处理插入。

public class Program
{
    public static void Main()
    {
        var array = new[] { "Hello" };
        array = array.Insert("World");
    }
}

public static class DummyExtension
{
    public static T Insert<T>(this IList<T> list, T insertValue)
    {
        list.Add(insertValue);
        return insertValue;
    }

    public static T[] Insert<T>(this T[] list, T insertValue)
    {

        var destArray = new T[list.Length + 1];
        Array.Copy(list, destArray, list.Length);
        destArray[destArray.Length - 1] = insertValue;

        return destArray;
    }
}

好吧,我同意这可能是一种粗略的方法,但它适用于您的情况。

您可以将扩展方法添加到 List<T> 而不是 IList<T>