C#通用重载List <t>:如何做?</t>

时间:2010-10-01 16:55:05

标签: c# generics overloading

StringBuilder类允许您以非常直观的方式将方法调用链接到.Append(),. AppendFormat()以及其他类似的方法:

StringBuilder sb = new StringBuilder();
sb.Append("first string")
  .Append("second string);

另一方面,List类'.Add()方法返回void - 因此链接调用不起作用。在我看来,这是Jayne Cobb的不朽言辞“只是不要有任何意义”。

我承认我对Generics的理解非常基础,但我想重载.Add()方法(和其他方法),以便它们返回原始对象,并允许链接。任何和所有的帮助都会得到进一步的萤火虫报价。

7 个答案:

答案 0 :(得分:13)

如果要为Add方法保留相同的名称,可以从基类隐藏方法:

public class MyList<T> : List<T>
{
    public new MyList<T> Add(T item)
    {
        base.Add(item);
        return this;
    }
}

但是,只有当您使用明确键入为MyList<T>的变量操作列表时才会起作用(例如,如果您的变量声明为IList<T>则不起作用)。所以我认为涉及扩展方法的解决方案更好,即使这意味着更改方法的名称。

虽然其他人已经发布了带有扩展方法的解决方案,但这是另一个,它具有保留集合的实际类型的优势:

public static class ExtensionMethods
{
    public static TCollection Append<TCollection, TItem>(this TCollection collection, TItem item)
        where TCollection : ICollection<TItem>
    {
        collection.Add(item);
        return collection;
    }
}

使用它:

var list = new List<string>();
list.Append("Hello").Append("World");

答案 1 :(得分:5)

使用可以创建扩展方法

public static class ListExtensions
{
    public static List<T> AddItem<T>(this List<T> self, T item)
    {
        self.Add(item);
        return self;
    }
}

var l = new List<int>();
l.AddItem(1).AddItem(2);

修改

我们也可以使这个方法通用于集合参数

public static class ListExtensions
{   
    public static TC AddItem<TC, T>(this TC self, T item)
        where TC : ICollection<T>
    {
        self.Add(item);
        return self;
    }
}

var c1 = new Collection<int>();
c1.AddItem(1).AddItem(2);

var c2 = new List<int>();
c2.AddItem(10).AddItem(20);

编辑2 : 也许有人会发现这个技巧很有用,可以使用嵌套对象初始化器和集合初始化器来设置属性并将值添加到现有实例中。

using System;
using System.Collections.Generic;
using System.Linq;

struct I<T>
{
    public readonly T V;
    public I(T v)
    {
        V = v;
    }
}

class Obj
{
    public int A { get; set; }
    public string B { get; set; }

    public override string ToString()
    {
        return string.Format("A={0}, B={1}", A, B);
    }
}


class Program
{
    static void Main()
    {
        var list = new List<int> { 100 };
        new I<List<int>>(list)
            {
                V = { 1, 2, 3, 4, 5, 6 }
            };

        Console.WriteLine(string.Join(" ", list.Select(x => x.ToString()).ToArray())); // 100 1 2 3 4 5 6 

        var obj = new Obj { A = 10, B = "!!!" };
        Console.WriteLine(obj); // A=10, B=!!!
        new I<Obj>(obj)
            {
                V = { B = "Changed!" }
            };
        Console.WriteLine(obj); // A=10, B=Changed!
    }
}

答案 2 :(得分:2)

public static IList<T> Anything-not-Add*<T>(this IList<T> list, T item)
{
    list.Add(item);
    return list;
}

* AddItemAppendAppendList等(请参阅下面的评论)

同样的想法也像其他人一样独立地出现在我的脑海中:

public static TList Anything<TList, TItem>(this TList list, TItem item)
    where TList : IList<TItem>
{
    list.Add(item);
    return list;

}

托马斯是对的:就IList<T>继承ICollection<T>而言,你应该使用ICollection。

答案 3 :(得分:1)

关闭扩展程序:

public static List<T> Append(this List<T> list, T item)
{
  list.Add(item);
  return self;
}

请注意,我们必须使用新名称创建它,就好像实例成员匹配签名(您已经在抱怨的'添加'),然后不会调用扩展方法。

总而言之,我建议不要这样做。虽然我喜欢链接自己,但它在C#库中很少见,这意味着它不像其他语言中那样惯用(更常见的是没有技术原因,尽管在属性工作方面的一些差异在其他一些语言中有点鼓励它) ,就像常见的事情一样。因此,它所支持的构造在C#中并不像其他地方那样熟悉,并且你的代码更容易被另一个开发者误读。

答案 4 :(得分:1)

您可以使用具有不同名称的扩展方法:

public static T Put<T, U>(this T collection, U item) where T : ICollection<U> {
  collection.Add(item);
  return collection;
}

要创建这样的代码:

var list = new List<int>();
list.Put(1).Put(2).Put(3);

要保留名称Add,您可以使用以下方法:

public static T Add<T, U>(this T collection, Func<U> itemProducer) 
  where T : ICollection<U> {
  collection.Add(itemProducer());
  return collection;
}

并创建如下代码:

list.Add(()=>1).Add(()=>2).Add(()=>3);

虽然看起来不那么好。

也许如果我们改变类型,我们可以有更好的语法。

鉴于此课程:

public class ListBuilder<T> {
  IList<T> _list;
  public ListBuilder(IList<T> list) {
    _list = list;
  }
  public ListBuilder<T> Add(T item) {
    _list.Add(item);
    return this;
  }
}

您可以使用此方法:

public static ListBuilder<T> Edit<T>(this IList<T> list) {
  return new ListBuilder<T>(list);
}

使用这样的代码:

list.Edit().Add(1).Add(2).Add(3);

答案 5 :(得分:1)

我确定你不会理解这个答案,但List&lt;&gt; .Add()以这种方式工作是有充分理由的。它非常快,它需要与阵列竞争,因为它是一种低级方法。然而,这只是一个太大而无法通过JIT优化器进行内联的头发。它无法优化返回列表引用所需的return语句。

在代码中编写lst.Add(obj)是免费的,第一个引用可以在CPU寄存器中找到。

返回引用的Add()版本使代码几乎慢了5%。对于提议的扩展方法来说,情况要糟糕得多,还有一个额外的堆栈帧。

答案 6 :(得分:0)

我喜欢其他人提到的扩展方法,因为它似乎很好地回答了这个问题(尽管你必须给它一个与现有Add()不同的方法签名)。此外,看起来像这样的调用上的对象返回有一些不一致(我认为它是一个可变性问题,但是字符串构建器是可变的不是吗?),所以你提出了一个有趣的问题。

但是,如果AddRange方法无法作为开箱即用的解决方案,我很好奇吗?您是否有特殊原因需要链接命令而不是将所有内容作为数组传递?

会做这样的事情没有达到你的需要吗?

List<string> list = new List<string>();
list.AddRange(new string[]{
    "first string", 
    "second string",
});