2010-03-20 43 views
6

OK Tôi đang tìm một số đầu vào, tôi khá chắc chắn điều này hiện không được hỗ trợ trong .NET 3.5 nhưng ở đây đi.Làm thế nào để làm việc xung quanh giới hạn trong các ràng buộc loại chung trong C#?

Tôi muốn yêu cầu một kiểu generic thông qua vào lớp học của tôi để có một constructor như thế này:

new(IDictionary<string,object>) 

vì vậy lớp sẽ trông như thế này

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

Nhưng trình biên dịch không hỗ trợ điều này, nó không thực sự biết những gì tôi yêu cầu.

Một số bạn có thể hỏi, tại sao bạn muốn làm điều này? Vâng tôi đang làm việc trên một dự án vật nuôi của một ORM vì vậy tôi nhận được các giá trị từ DB và sau đó tạo đối tượng và tải các giá trị.

Tôi nghĩ rằng nó sẽ sạch hơn để cho phép đối tượng chỉ tạo ra chính nó với các giá trị tôi đưa ra. Theo như tôi có thể nói tôi có hai lựa chọn:

1) Sử dụng phản chiếu (mà tôi đang cố tránh) để lấy mảng PropertyInfo [] và sau đó sử dụng để tải các giá trị.

2) yêu cầu T để hỗ trợ một giao diện như sau:

ILoadValues ​​public interface { trống LoadValues ​​(giá trị IDictionary); }

và sau đó làm điều này

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

Tôi có vấn đề với giao diện Tôi đoán là triết học, tôi không thực sự muốn để lộ một phương pháp nào để mọi người tải các giá trị. Sử dụng các nhà xây dựng ý tưởng là nếu tôi có một đối tượng như thế này

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

Chừng nào các MyClass<T> là trong cùng một assembly các nhà xây dựng sẽ được cung cấp. Cá nhân tôi nghĩ rằng ràng buộc Loại theo ý kiến ​​của tôi nên hỏi (Tôi có quyền truy cập vào nhà xây dựng này không? Tôi làm, tuyệt vời!)

Bất kỳ đầu vào nào cũng được chào đón.

Trả lời

2

Nếu bạn có thể tạo lớp cơ sở chung cho tất cả các ojects T rằng bạn sẽ vượt qua để MyClass như tham số kiểu hơn bạn có thể làm như sau:

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; 
    } 
} 

Nếu bạn không thể có lớp cơ sở chung hơn tôi nghĩ bạn nên đi với giải pháp được đề xuất bởi itowlson.

+0

Tôi đặt bạn làm câu trả lời vì tôi không bao giờ biết bạn có thể ẩn một triển khai giao diện bằng cách tuyên bố rõ ràng nó. Hoặc ít nhất nó đã không bấm cho đến khi bạn đã viết nó. Cảm ơn! – Jose

1

Bạn không thể làm điều đó. Các ràng buộc new (constructor) chỉ dành cho các hàm tạo ít tham số. Bạn không thể có một ràng buộc cho một hàm tạo với các tham số cụ thể.

Tôi đã gặp vấn đề tương tự, và cuối cùng tôi đã quyết định thực hiện phần "tiêm" thông qua một phương pháp được cung cấp trong một giao diện được liệt kê dưới dạng ràng buộc (như được minh họa trong mã).

(Tôi hy vọng ai đó ở đây đã tìm thấy một câu trả lời tao nhã hơn cho vấn đề này!)

8

Như stakx đã nói, bạn không thể làm điều này với một hạn chế chung.Một cách giải quyết Tôi đã sử dụng trong quá khứ là phải có các nhà xây dựng lớp generic mất một phương pháp nhà máy mà nó có thể sử dụng để xây dựng T:

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); 
    } 
} 

Được sử dụng như sau:

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

này mang đến cho bạn biên dịch an toàn kiểu thời gian, với chi phí của một mô hình xây dựng hơi phức tạp hơn, và khả năng ai đó có thể truyền cho bạn một đại biểu không thực sự tạo ra một đối tượng mới (có thể hoặc không phải là vấn đề lớn tùy thuộc vào mức độ nghiêm ngặt của bạn muốn ngữ nghĩa của CreateObject được).

+0

+1 (nếu có thể). Đây là một chút trên mức lương của tôi và tôi không thể thực sự nhà nước với sự tự tin rằng điều này là chính xác (mà tôi chắc chắn nó là). Tuy nhiên, ví dụ của bạn là cực kỳ rõ ràng và khá dễ làm theo. Vâng, thưa ngài! –

1

Tôi tò mò về cách bạn sẽ tải các giá trị của một lớp mà không sử dụng sự phản chiếu trừ khi bạn có các phương thức được mã hóa cứng để thực hiện nó. Tôi chắc chắn có một câu trả lời khác, nhưng tôi không quá xấu hổ khi nói rằng tôi không có kinh nghiệm trong đó. Đối với một cái gì đó tôi đã viết để tự động tải dữ liệu, tôi có hai lớp dữ liệu cơ bản tôi làm việc từ: một đối tượng duy nhất và sau đó là một danh sách. Trong đối tượng đơn (BaseDataClass), tôi có phương thức này.

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; 
        } 
       } 
      } 
     } 
    } 

Và sau đó trong danh sách dữ liệu

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); 
      } 
     } 
    } 
} 

Tôi mở cửa cho những lời chỉ trích, bởi vì không ai có thể xem xét mã này trước đó. Tôi là lập trình viên duy nhất mà tôi làm việc, vì vậy tôi không có người khác để trả lại ý tưởng. Rất may, bây giờ tôi đã đi qua trang web này!

Chỉnh sửa: Bạn biết không? Nhìn vào nó ngay bây giờ, tôi không hiểu tại sao tôi không nên viết lại phương thức cuối cùng đó là

 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); 
       } 
      } 
     } 

Tôi cho rằng công trình, mặc dù tôi chưa thử nghiệm nó. Không cần phải sử dụng sự phản chiếu trên phần đó.

+0

Nếu bạn sử dụng DataSets hoặc Typed DataSets, tại sao bạn sẽ không sử dụng MetaData trong này, Nếu bạn sử dụng một typed dataSet, bạn sẽ biết dữ liệu là gì trước khi bạn bắt đầu phản ánh. Tại sao lại sử dụng ORM nếu bạn sử dụng DataSets? DataSets cũng có vấn đề với dữ liệu lớn, tốt hơn bằng cách sử dụng một DataReader và streaming dữ liệu từ từ giữ phân bổ bộ nhớ xuống đến mức tối thiểu. – WeNeedAnswers

+0

@Anthony Câu trả lời hay, mặc dù tốt để thấy rằng mọi người quan tâm đủ để trả lời đầy đủ :) – WeNeedAnswers

+0

Tôi thường không sử dụng DataSets, mà là đọc thẳng vào một DataTable. Tôi cho rằng tôi có thể sử dụng một người đọc, nhưng tôi luôn nghĩ rằng nó đơn giản hơn nếu tôi cần truyền đối tượng xung quanh để sử dụng DataTable/DataRow thay vì giữ kết nối cơ sở dữ liệu mở. Nhưng, như tôi đã nói, tôi mở cửa cho những lời chỉ trích và cảm ơn phản hồi của bạn. –

Các vấn đề liên quan