Менее определенные дженерики в C #? - PullRequest
3 голосов
/ 03 декабря 2009

Есть ли способ использовать коллекцию универсального класса, не предоставляя базовый тип? Давайте объясним:

Вот что я хотел бы иметь:

class TimeSerie<TValue> {
enter code here
}

List<TimeSerie<?>> blah;

Вот что я должен сделать:

class TimeSerie {}
class TypedTimeSerie<TValue> : TimeSerie {}

List<TimeSerie> blah;

Итак, есть ли способ использовать хорошее первое решение? (хотя я предполагаю, что это вызовет проблемы при попытке привести, например, к циклу ...)

Ответы [ 5 ]

8 голосов
/ 03 декабря 2009

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

1 голос
/ 07 декабря 2009

Обратите внимание, что возможно некоторое "бормотание" по отношению к анонимным типам с помощью c # благодаря двум вещам:

  1. Тип вывода
  2. объединение идентичных анонимных типов

Если вы счастливы полагаться на то, что эти две вещи остаются неизменными (на это нет никаких гарантий, особенно в отношении 2), тогда может пригодиться следующее.

public static class Mumble 
{
    public static HashSet<T> HashSet<T>(T prototype)
    {
        return new HashSet<T>();
    }

    public static List<T> List<T>(T prototype)
    {
        return new List<T>();
    } 
}

Вы можете использовать его так:

var set = MumbleSet(new { Foo="", Bar="", Baz=0 });
var list = MumbleList(new { Foo="", Bar="", Baz=0 });
set.Add(new { Foo="x", Bar="y", Baz=1 });
set.Add(new { Foo="a", Bar="b", Baz=1 });
list.Add(new { Foo="a", Bar="b", Baz=1 });
var intersection = list.Intersect(set);
var concat = list.Concat(set); 

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

Для вашего примера мотивации вы должны добавить следующее:

class TimeSerie<TValue> 
{   
    // or some other constructor equivalent 
    public TimeSerie(TValue value) { /* assign the value */ }
}

static class TimeSerieMumble
{
    public static TimeSerie<TValue> New<TValue>(TValue value)
    {
        return new TimeSerie<TValue>(value);
    }
}

Тогда вы можете использовать код так:

var tsList = Mumble.List(TimeSerieMumble.New(new { Name="", Value=0 }));
foreach (var x in from c select new { c.Name, c.Value })
{
    tsList.Add(TimeSerieMumble.New(new { x.Name, x.Value }));
}

Бормочить, что «просачивается» в публичный API, невозможно в c # 3.5, если только тип не должен быть бормотан через серию логических методов вывода типа таким же образом, как в вышеприведенном примере. Я никогда не видел случая, чтобы такая вещь была полезной, учитывая результирующие искажения, необходимые для вызывающего кода. Я не думаю, что это улучшит читабельность либо. Как правило, использование более чем двух уровней бормотания в примере «Имя / Значение» может привести к серьезным осложнениям в будущем.

1 голос
/ 03 декабря 2009

Почему бы и нет?

List<TimeSerie<Object>> blah;

Тогда после того, как вы укажете свой объект. Также определите ваш базовый класс соответственно.

1 голос
/ 03 декабря 2009

Я не вижу, основываясь на вашем вопросе, почему вы не можете получить свою собственную коллекцию из ICollection<T> или List<T> (или, возможно, наследовать от ICollection и делегировать вызовы в поле типа List<T>, которое вы храните внутри?

(вполне возможно, что я просто не понимаю, но не могли бы вы дать чуть больше примера кода?)

0 голосов
/ 07 декабря 2009

Как уже говорили другие, в C # нет простого способа сделать это. Однако, если это действительно важно, можно точно кодировать этот шаблон, используя несколько дополнительных типов, хотя это немного уродливо:

interface ITimeSeriesUser<X> {
    X Use<T>(TimeSeries<T> series);
}

interface ITimeSeriesUser {
    void Use<T>(TimeSeries<T> series);
}

interface ITimeSeries {
    X Apply<X>(ITimeSeriesUser<X> user);
    void Apply(ITimeSeriesUser user);
}

class TimeSeries<T> : ITimeSeries {
    X Apply<X>(ITimeSeriesUser<X> user) { return user.Use(this); }
    void Apply(ITimeSeriesUser user) { return user.Use(this); }

    /* Your existing code goes here */
}

Теперь вы можете создать List<ITimeSeries> экземпляр, который содержит TimeSeries<T> значения независимо от их аргументов типа, и вы можете использовать ITimeSeriesUser реализации, чтобы манипулировать ими. Очевидно, что это требует довольно много шаблонного, но если вам нужен верный способ выразить концепцию TimeSeries<?>, тогда это может быть вашим лучшим выбором.

...