2010-03-20 23 views
6

Tamam Bazı girişler arıyorum, şu anda .NET 3.5'de desteklenmediğinden eminim ama işte geliyor.C# Genel Bağlantı Kısıtlamaları Kısıtlamaları Çevresi Nasıl Çalışır?

Böyle bir yapıcı olması benim sınıfa geçti genel bir tür gerektiren istiyorum:

new(IDictionary<string,object>) 

yüzden sınıf bu

public MyClass<T> where T : new(IDictionary<string,object>) 
{ 
    T CreateObject(IDictionary<string,object> values) 
    { 
    return new T(values); 
    } 
} 

gibi görünecektir Ama derleyici desteklemiyor Bu, ne sorduğumu gerçekten bilmez.

Bazılarınız bunu sorabilir, neden bunu yapmak istiyorsunuz? Peki bir ORM'nin bir evcil hayvan projesinde çalışıyorum, böylece DB'den değerler alıyorum ve sonra nesneyi oluşturup değerleri yüklüyorum.

Nesnenin verdiğim değerlerle kendini yaratmasına izin vermenin daha temiz olacağını düşündüm. İki seçeneğim olduğunu söyleyebilirim:

1) PropertyInfo [] dizisini almak için yansımayı (kaçınmaya çalıştığım) kullanın ve ardından değerleri yüklemek için bunu kullanın.

2) çok gibi bir arabirim desteklemek için T gerektirir:

ortak arabirim ILoadValues ​​ { void LoadValues ​​(IDictionary değerleri); }

ve sonra ben felsefi olduğunu tahmin arayüzü ile sahip bu

public MyClass<T> where T:new(),ILoadValues 
{ 
    T CreateObject(IDictionary<string,object> values) 
    { 
    T obj = new T(); 
    obj.LoadValues(values); 
    return obj; 
    } 
} 

sorunu yapmak, gerçekten insanlar değerleri yüklemek için genel bir yöntem ortaya çıkarmak istemeyiz. yapıcısı kullanarak Fikir MyClass<T> aynı mecliste olduğu gibi sürece bu

namespace DataSource.Data 
{ 
    public class User 
    { 
    protected internal User(IDictionary<string,object> values) 
    { 
     //Initialize 
    } 
    } 
} 

gibi bir nesne olsaydı yapıcı mevcut olacağıydı. Ben şahsen benim düşünceme Tip kısıtlama sormak gerekir (Ben bu kurucu erişim var mı? Yapmak, harika!)

Herhangi bir girdi hoş geldiniz.

cevap

2

Eğer aşağıdakileri yapabilirsiniz daha tip parametreleri olarak Sınıfım geçmek gidiyoruz T ojects tümü için aynı temel sınıfa oluşturabiliyorsanız itowlson tarafından önerilen çözümle gitmelisiniz.

+0

Sizi yanıt olarak ayarladım çünkü açık bir şekilde bildirerek bir arabirim uygulamasını gizleyebildiğinizi asla bilemedim. Ya da en azından yazana kadar tıklamadı. Teşekkürler! – Jose

1

Bunu yapamazsınız. new (yapıcı) kısıtlamaları yalnızca parametresiz kurucular içindir. Belirli parametrelere sahip bir kurucu için bir kısıtlama olamaz.

Gerçi aynı sorunla karşılaştık, ve nihayet gösterildiği gibi bir kısıtlama olarak listelenir arayüzleri birinde (sağlanır bir yöntemle "enjeksiyon" parçası yapıyor oturttuktan senin kodu).

(burada birileri bu soruna daha şık bir cevap buldu umut!) Stakx belirttiği gibi

8

, bir genel kısıtlaması ile bunu yapamaz.aşağıdaki gibi

public class MyClass<T> 
{ 
    public delegate T Factory(IDictionary<string, object> values); 

    private readonly Factory _factory; 

    public MyClass(Factory factory) 
    { 
    _factory = factory; 
    } 

    public T CreateObject(IDictionary<string, object> values) 
    { 
    return _factory(values); 
    } 
} 

Kullanılan: Bu verir

MyClass<Bob> instance = new MyClass<Bob>(dict => new Bob(dict)); 
Bob bob = instance.CreateObject(someDictionary); 

ben geçmişte kullandığınız bir geçici çözüm jenerik sınıf yapıcısı o T oluşturmak için kullanabileceğiniz bir fabrika yöntemini almak sahip olmaktır biraz daha kıvrık bir yapı paterni pahasına, zaman tipi emniyeti derleyin ve bir başkasının size yeni bir obje yaratmadığı bir delege aktarma olasılığı (ne kadar sıkı olduğuna bağlı olarak büyük bir sorun olabilir veya olmayabilir) CreateObject semantiği olmasını istiyorum). Bence daha ortak temel sınıfa sahip olamıyorsan

internal interface ILoadValues 
{ 
    void LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values); 
} 

public class Base : ILoadValues 
{ 
    void ILoadValues.LoadValues<TKey, TValue>(IDictionary<TKey, TValue> values) 
    { 
     // Load values. 
    } 
} 

public class MyClass<T> 
    where T : Base, new() 
{ 
    public T CreateObject(IDictionary<string,object> values) 
    { 
     ILoadValues obj = new T(); 
     obj.LoadValues(values); 
     return (T)obj; 
    } 
} 

:

+0

+1 (daha fazla bilgi için). Bu, benim ödeme notumun biraz üzerindeydi ve bunun doğru olduğuna emin olamadım (ki eminim ki). Ancak, örneğiniz son derece net ve takip edilmesi oldukça kolaydır. İyi dedin efendim! –

1

Sana bunu başarmak için kodlanmış yöntemleri vardı sürece yansıma kullanmadan bir sınıfın değerlerini yük olacağını nasıl meşru merak ediyorum. Eminim başka bir cevap var, ama bende tecrübem yok demekten utanmıyorum. Verileri otomatik olarak yazmak için yazdığım bir şey için, çalıştığım iki temel veri sınıfım var: tek bir nesne ve daha sonra bir liste. Tek nesnedeki (BaseDataClass), bu yönteme sahibim.

public virtual void InitializeClass(DataRow dr) 
    { 
     Type type = this.GetType(); 
     PropertyInfo[] propInfos = type.GetProperties(); 

     for (int i = 0; i < dr.ItemArray.GetLength(0); i++) 
     { 
      if (dr[i].GetType() != typeof(DBNull)) 
      { 
       string field = dr.Table.Columns[i].ColumnName; 
       foreach (PropertyInfo propInfo in propInfos) 
       { 
        if (field.ToLower() == propInfo.Name.ToLower()) 
        { 
         // get data value, set property, break 
         object o = dr[i]; 
         propInfo.SetValue(this, o, null); 
         break; 
        } 
       } 
      } 
     } 
    } 

Sonra veri listesindeki

public abstract class GenericDataList<T> : List<T> where T : BaseDataClass 
{ 
    protected void InitializeList(string sql) 
    { 
     DataHandler dh = new DataHandler(); // my general database class 
     DataTable dt = dh.RetrieveData(sql); 
     if (dt != null) 
     { 
      this.InitializeList(dt); 
      dt.Dispose(); 
     } 
     dt = null; 
     dh = null; 
    } 

    protected void InitializeList(DataTable dt) 
    { 
     if (dt != null) 
     { 
      Type type = typeof(T); 
      MethodInfo methodInfo = type.GetMethod("InitializeClass"); 

      foreach (DataRow dr in dt.Rows) 
      { 
       T t = Activator.CreateInstance<T>(); 
       if (methodInfo != null) 
       { 
        object[] paramArray = new object[1]; 
        paramArray[0] = dr; 
        methodInfo.Invoke(t, paramArray); 
       } 

       this.Add(t); 
      } 
     } 
    } 
} 

daha önce hiç kimsenin bu kodu gözden geçirmiş, çünkü eleştiriye açığım içinde

. Çalıştığım tek programcıyım, bu yüzden fikirlerini geri çekecek başkalarım yok. Neyse ki, şimdi bu web sitesine rastladım!

Düzenleme: Sen ne biliyorsun? şimdi bakınca ben sadece işleri, bunu test edilmemiş olmasına rağmen varsayalım

 protected void InitializeList(DataTable dt) 
     { 
      if (dt != null) 
      { 
       Type type = typeof(T); 

       foreach (DataRow dr in dt.Rows) 
       { 
        T t = Activator.CreateInstance<T>(); 
        (t as BaseDataClass).InitializeClass(dr); 

        this.Add(t); 
       } 
      } 
     } 

olarak son yöntemi yeniden olmamalıdır neden görmüyorum. Bu kısımdaki yansımayı kullanmaya gerek yok.

+0

DataSets veya Typed DataSet'leri kullanıyorsanız, neden MetaData'yı bunlarda kullanmıyorsunuz, Yazdığınız bir dataSet'i kullanıyorsanız, yansıtmaya başlamadan önce verilerin ne olduğunu bileceksiniz. DataSet'leri kullanacaksanız neden bir ORM kullanıyorsunuz? DataSets ayrıca büyük verilerle ilgili problemler yaşar, DataReader kullanımı daha iyi olur ve verileri yavaşça bellek ayırmayı en aza indirir. İyi olsa – WeNeedAnswers

+0

@Anthony Güzel cevap, insanlar genellikle DataSets kullanmayan bir cevap :) – WeNeedAnswers

+0

tamamlamak böylece vermek için yeteri kadar önem görüyoruz, bunun yerine düz bir DataTable okumak için. Bir okuyucu kullanabileceğimi sanıyorum, ancak bir veritabanı bağlantısını açık tutmak yerine, bir DataTable/DataRow kullanmak için nesneyi iletmem gerekirse daha basit olduğunu düşündüm. Ama dediğim gibi, eleştirime açığım ve geri bildiriminiz için teşekkürler. –