Методы расширения цепочки в C # - PullRequest
7 голосов
/ 09 июля 2010

Можно ли создать метод расширения, который возвращает экземпляр, который вызывает метод расширения?

Я хотел бы иметь метод расширения для всего, что наследуется от ICollection<T>, возвращает объект.Очень похоже на то, как jQuery всегда возвращает объект jquery.

public static object AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
{

Я представляю что-то подобное выше, но я не уверен, как вернуться к родительскому типу объекта "this" для использования чего-то подобного:

List<int> myInts = new List<int>().AddItem(5);

РЕДАКТИРОВАТЬ: Просто хотел быть ясно, что я надеялся на одно общее решение ограничения.

Ответы [ 5 ]

13 голосов
/ 09 июля 2010

Если вам нужно вернуть определенный тип, вы можете использовать общее ограничение:

public static TCollection AddItem<TCollection, TElement>(
    this TCollection collection,
    TElement itemToAdd)
    where TCollection : ICollection<TElement>
{
    collection.Add(itemToAdd);
    return collection;
}

Я проверил это, и оно работает в VS2010.

Обновление (относительно jQuery):

jQuery цепочка работает очень хорошо, потому что JavaScript использует динамическую типизацию. C # 4.0 поддерживает dynamic, поэтому вы можете сделать это:

public static dynamic AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
}

Однако я рекомендую универсальную версию ограничения, поскольку она более безопасна для типов, более эффективна и позволяет использовать IntelliSense для возвращаемого типа. В более сложных сценариях общие ограничения не всегда способны выразить то, что вам нужно; в этих случаях можно использовать dynamic (хотя он не будет привязываться к дополнительным методам расширения, поэтому он плохо работает с цепочкой).

4 голосов
/ 09 июля 2010

Хотя у меня нет открытой VS, чтобы попробовать это, что-то вроде этого должно работать:

public static TCollection AddItem<TCollection, TItem>(TCollection collection, 
                                                      TItem itemToAdd) 
where TCollection : ICollection<TItem>
{
    collection.Add(itemToAdd);
    return collection;
}
2 голосов
/ 10 июля 2010

У вас, похоже, две противоречивые цели, и все сводится к тому, что вы хотите, чтобы ваш метод расширения возвращал:

  • Экземпляр, который вызвал метод расширения (коллекцию)
  • ИЛИ элемент, который был добавлен в коллекцию

Из вашего примера использования, приведенного здесь:

List<int> myInts = new List<int>().AddItem(5);

Похоже, вы хотите вернуть коллекцию.В любом случае это назначение по-прежнему не будет работать без приведения, поскольку ваш метод расширения должен иметь возвращаемый тип ICollection, например:

public static ICollection<T> AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return collection;
}

Это позволит вам сделать это:

List<int> myList = (List<int>) new List<int>().AddItem(5);

Теперь, если вы хотите вернуть объект, который был добавлен, вы все равно не должны иметь возвращаемый тип object.Вы должны воспользоваться преимуществом параметра универсального типа и вернуть T, например:

public static T AddItem<T>(this ICollection<T> collection, T itemToAdd)
{
    collection.Add(itemToAdd);
    return itemToAdd;
}

Однако, если вы возвращаете элемент, который был добавлен, вы не сможете соединиться какэто:

List<int> myList = (List<int>) new List<int>().AddItem(5);

, поскольку тип возвращаемого значения AddItem(5) равен , а не ICollection, но это T (int, в данном случае).Тем не менее, вы можете по-прежнему цепочки, только за счет добавленной стоимости, например:

List<int> myList = new List<int>();
myList.AddItem(5).DoSomethingWithMyInt(); // Not very useful in this case

Кажется, что первый сценарий более полезен (возврат коллекции), потому что он позволяет вам цепочки, сразу женачальный оператор присваивания.Вот более крупный пример этого:

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

Или, если вы не хотите разыгрывать, вы можете вызвать ToList() на ICollection, которая возвращается:

List<int> myList = new List<int>().AddItem(1).AddItem(2).ToList();
1 голос
/ 10 июля 2010

РЕДАКТИРОВАТЬ: Просто хотел быть ясно, что я надеялся на одно общее решение ограничения.

В этом случае вам не повезло, потому что преобразования возвращаемого типа могут быть ковариантными, но не контравариантными (то есть вы не можете неявно преобразовать из ICollection<T> в List<T>), поэтому без универсального возвращаемого типа это невозможно сделать.

Что не так с указанием двух параметров типа? Они могут быть выведены из аргументов, которые вы предоставляете функции, поэтому вы даже не заметите их в своем коде вызова.

0 голосов
/ 10 июля 2010

Просто верните ICollection<T> вместо object и все должно работать так, как вы хотели.

...