C # Generics вопрос - PullRequest
       13

C # Generics вопрос

3 голосов
/ 23 апреля 2009

Я немного устал от дженериков, пытаясь сделать следующее, но компилятор жалуется:

protected List<T> PopulateCollection(DataTable dt) where T: BusinessBase
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T(dr);
        lst.Add(t);
    }
    return lst;
}

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

Разве это не возможно?

Ответы [ 6 ]

21 голосов
/ 23 апреля 2009

Есть две большие проблемы:

  • Нельзя указать ограничение конструктора, которое принимает параметр
  • Ваш метод в настоящее время не является универсальным - он должен быть PopulateCollection<T> вместо PopulateCollection.

У вас уже есть ограничение T : BusinessBase, поэтому, чтобы обойти первую проблему, я предлагаю вам добавить абстрактный (или виртуальный) метод в BusinessBase:

public abstract void PopulateFrom(DataRow dr);

Также добавьте ограничение конструктора без параметров к T.

Ваш метод может стать:

protected List<T> PopulateCollection(DataTable dt)
    where T: BusinessBase, new()
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T();
        t.PopulateFrom(dr);
        lst.Add(t);
    }
    return lst;
}

Если вы используете .NET 3.5, вы можете сделать это немного проще, используя метод расширения в DataTableExtensions:

protected List<T> PopulateCollection<T>(DataTable dt)
    where T: BusinessBase, new()
{
    return dt.AsEnumerable().Select(dr => 
    { 
        T t = new T();
        t.PopulateFrom(dr);
    }.ToList();
}

В качестве альтернативы, вы можете сделать его методом расширения (опять же, при условии .NET 3.5) и передать функцию для возврата экземпляров:

static List<T> ToList<T>(this DataTable dt, Func<DataRow dr, T> selector)
    where T: BusinessBase
{
    return dt.AsEnumerable().Select(selector).ToList();
}

Ваши абоненты будут писать:

table.ToList(row => new Whatever(row));

Предполагается, что вы вернетесь к получению конструктором DataRow. Это дает вам преимущество, заключающееся в том, что вы можете писать неизменяемые классы (и те, у которых нет конструктора без параметров), но означает, что вы не можете работать в общем случае, не имея также функции «factory».

3 голосов
/ 23 апреля 2009

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

interface ISupportInitializeFromDataRow
{
    void InitializeFromDataRow(DataRow dataRow);
}

protected List<T> PopulateCollection<T>(DataTable dt) 
    where T : BusinessBase, ISupportInitializeFromDataRow, new()
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = new T();
        t.InitializeFromDataRow(dr);

        lst.Add(t);
    }
    return lst;
}

Или

protected List<T> PopulateCollection<T>(DataTable dt, Func<DataRow, T> builder) 
    where T : BusinessBase
{
    List<T> lst = new List<T>();
    foreach (DataRow dr in dt.Rows)
    {
        T t = builder(dr);        
        lst.Add(t);
    }
    return lst;
}
2 голосов
/ 23 апреля 2009

Возможный путь:

protected List<T> PopulateCollection<T>(DataTable dt) where T: BusinessBase, new()
    {
        List<T> lst = new List<T>();
        foreach (DataRow dr in dt.Rows)
        {
            T t = new T();
            t.DataRow = dr;
            lst.Add(t);
        }
        return lst;
    }
2 голосов
/ 23 апреля 2009

Возможно, вам нужно добавить общее ограничение new на T следующим образом:

protected List<T> PopulateCollection<T>(DataTable dt) where T : BusinessBase, new()
...

Я не могу передать DataRow в конструктор, но вы можете решить это, присвоив ему свойство BusinessBase

0 голосов
/ 23 апреля 2009

Это возможно. У меня точно такая же вещь в моей структуре. У меня была точно такая же проблема, как и у вас, и вот как я ее решил. Размещение соответствующих фрагментов из рамок. Насколько я помню, самой большой проблемой было требование вызова конструктора без параметров.

 public class Book<APClass> : Book where APClass : APBase
        private DataTable Table ; // data
        public override IEnumerator GetEnumerator()
        {                        
            for (position = 0;  position < Table.Rows.Count;  position++)           
                 yield return APBase.NewFromRow<APClass>(Table.Rows[position], this.IsOffline);
        }
   ...


  public class APBase ...
  {
    ...
    internal static T NewFromRow<T>(DataRow dr, bool offline) where T : APBase
        {

            Type t = typeof(T);
            ConstructorInfo ci;

            if (!ciDict.ContainsKey(t))
            {
                ci = t.GetConstructor(new Type[1] { typeof(DataRow) });
                ciDict.Add(t, ci);
            }
            else ci = ciDict[t];

            T result = (T)ci.Invoke(new Object[] { dr });

            if (offline)
                result.drCache = dr;    

            return result;
        }

В этом сценарии базовый класс имеет статический метод для создания экземпляров объектов своих производных классов с помощью конструктора, который принимает tablerow.

0 голосов
/ 23 апреля 2009
where T: BusinessBase

Должно быть ограничение new () Я думаю, что добавлено

...