2009-11-05 34 views
17

Tôi đang cố gắng để tạo ra một lớp chung chung mới lên một thể hiện của loại chung chung. Như sau:C# vấn đề generics - làm mới lên các loại chung với các tham số trong constructor

public class HomepageCarousel<T> : List<T> 
    where T: IHomepageCarouselItem, new() 
{ 
    private List<T> GetInitialCarouselData() 
    { 
     List<T> carouselItems = new List<T>(); 

     if (jewellerHomepages != null) 
     { 
      foreach (PageData pageData in jewellerHomepages) 
      { 
       T item = new T(pageData); // this line wont compile 
       carouselItems.Add(item); 
      } 
     } 
     return carouselItems; 
    } 
} 

Nhưng tôi nhận được lỗi sau:

cannot provide arguments when creating an instance of a variable type

Tôi tìm thấy câu hỏi có liên quan sau đó là rất gần với những gì tôi cần: Passing arguments to C# generic new() of templated type

Tuy nhiên, tôi có thể' Tôi đã sử dụng câu trả lời được đề xuất của Jared vì tôi là gọi phương thức trong lớp Chung, không nằm ngoài số , vì vậy tôi không thể chỉ định lớp cụ thể.

Có cách nào khác không?

Tôi đã thử những điều sau dựa trên câu hỏi khác, nhưng nó không hoạt động vì tôi không biết loại bê tông T là chỉ định. Vì nó được gọi từ bên trong lớp chung chung, không bên ngoài:

public class HomepageCarousel<T> : List<T> 
    where T: IHomepageCarouselItem, new() 
{ 

    private List<T> LoadCarouselItems() 
    { 
     if (IsCarouselConfigued) 
     { 
      return GetConfiguredCarouselData(); 
     } 

     // ****** I don't know the concrete class for the following line, 
     //  so how can it be instansiated correctly? 

     return GetInitialCarouselData(l => new T(l)); 
    } 

    private List<T> GetInitialCarouselData(Func<PageData, T> del) 
    { 
     List<T> carouselItems = new List<T>(); 

     if (jewellerHomepages != null) 
     { 
      foreach (PageData pageData in jewellerHomepages) 
      { 
       T item = del(pageData); 
       carouselItems.Add(item); 
      } 
     } 
     return carouselItems; 
    } 
} 

******** EDIT: THÊM GIẢI PHÁP CÓ THỂ **

Vì vậy, tôi đã thử nghiệm 2 giải pháp khả thi:

Đầu tiên chính xác như được giải thích dưới đây bởi Jon Skeet. Điều này chắc chắn hoạt động nhưng có nghĩa là có một lambda mơ hồ trong các nhà xây dựng . Tôi không phải là rất thoải mái với điều này vì nó có nghĩa là người dùng cần phải biết lambda chính xác đó là dự kiến. Sau khi tất cả, họ có thể vượt qua một lambda mà không mới lên các loại , nhưng làm một cái gì đó hoàn toàn bất ngờ

Thứ hai, tôi đã đi xuống tuyến đường phương pháp Nhà máy; Tôi đã thêm một Tạo phương pháp để giao diện chung:

IJewellerHomepageCarouselItem Create(PageData pageData); 

Sau đó cung cấp một thực hiện trong mỗi lớp bê tông:

public IJewellerHomepageCarouselItem Create(PageData pageData) 
{ 
    return new JewellerHomepageCarouselItem(pageData, null); 
} 

Và sử dụng một hai bước khởi cú pháp:

T carouselItem = new T(); 
T homepageMgmtCarouselItem = (T) carouselItem.Create(jewellerPage); 

Would thích nghe một số phản hồi về công đức của mỗi phương pháp này.

+0

bản sao có thể có của [Gửi đối số cho C# generic mới() của loại templated] (http://stackoverflow.com/questions/840261/passing – nawfal

Trả lời

18

câu trả lời Jared vẫn là một cách tốt để đi - bạn chỉ cần phải thực hiện các constructor lấy Func<PageData, T> và giấu nó cho sau này:

public class HomepageCarousel<T> : List<T> where T: IHomepageCarouselItem 
{ 
    private readonly Func<PageData, T> factory; 

    public HomepageCarousel(Func<PageData, T> factory) 
    { 
     this.factory = factory; 
    } 

    private List<T> GetInitialCarouselData() 
    { 
     List<T> carouselItems = new List<T>(); 

     if (jewellerHomepages != null) 
     { 
      foreach (PageData pageData in jewellerHomepages) 
      { 
       T homepageMgmtCarouselItem = factory(pageData); 
       carouselItems.Add(homepageMgmtCarouselItem); 
      } 
     } 
     return carouselItems; 
    } 

Sau đó, bạn chỉ cần vượt qua chức năng vào constructor nơi bạn tạo ra phiên bản mới của HomepageCarousel<T>.

(tôi khuyên bạn nên thành phần thay vì thừa kế, btw ... phát sinh từ List<T> là hầu như luôn luôn sai đường để đi.)

+1

Tôi chưa bao giờ thích cách này, và thường là mặc định cho kỹ thuật Activator (như đề xuất của Quintin) trừ khi sử dụng sự phản chiếu sẽ có tác động không thuận lợi. – philsquared

+0

cảm ơn Tony. Bạn có thể xây dựng cách bạn sẽ sử dụng bố cục thay vì thừa kế Tôi ban đầu có một thuộc tính được gọi là CarouselItems chứa dữ liệu. Nhưng sau đó thay đổi lớp để kế thừa từ Danh sách và làm cho dữ liệu có sẵn theo cách đó.
Tôi đoán bạn đang nói cả hai cách không tuyệt vời ?? – ChrisCa

+3

Nó không phải là tác động hiệu suất tôi nghĩ - đó là "không tìm ra những thứ bị phá vỡ cho đến khi thời gian thực hiện" khía cạnh. –

18

Bạn đã xem là sử dụng Activator (điều này chỉ là một tùy chọn).

T homepageMgmtCarouselItem = Activator.CreateInstance(typeof(T), pageData) as T; 
+1

có, tôi đã xem xét nó. Tôi đã đọc bài viết này. http://www.dalun.com/blogs/05.27.2007.htm Nhưng tôi không muốn đi theo cách đó nếu có thể tránh được. Tôi thích cú pháp được đề xuất trong câu hỏi khác Nhưng cảm ơn tất cả như nhau cho đề xuất – ChrisCa

0

Có một giải pháp khác có thể, thay vì bẩn.

Đặt IHomepageCarouselItem có phương thức "Xây dựng" lấy tham số pageData làm tham số và trả về IHomepageCarouselItem.

Sau đó làm điều này:

T factoryDummy = new T(); 
    List<T> carouselItems = new List<T>(); 

    if (jewellerHomepages != null) 
    { 
     foreach (PageData pageData in jewellerHomepages) 
     { 
      T homepageMgmtCarouselItem = (T)factoryDummy.Construct(pageData); 
      carouselItems.Add(homepageMgmtCarouselItem); 
     } 
    } 
    return carouselItems; 
1

Đó là một C# và CLR khuyết tật, bạn không thể vượt qua một cuộc tranh cãi đến mới T(), đơn giản.

Nếu bạn đến từ một nền C++, điều này được sử dụng là KHÔNG bị hỏng và TRIVIAL. PLUS bạn thậm chí không cần giao diện/ràng buộc. Phá vỡ tất cả các nơi, và không có nhà máy chức năng 3.0 hack bạn buộc phải làm 2-pass initialisation. Quản lý báng bổ!

Thực hiện T mới() trước và sau đó đặt thuộc tính hoặc chuyển cú pháp khởi tạo kỳ lạ hoặc tất cả đều được đề xuất sử dụng giải pháp chức năng của Pony .. Tất cả yucky nhưng đó là trình biên dịch và ý tưởng thời gian chạy của 'generics' cho bạn.

+1

Điểm chung là chung chung. Thực hiện một triển khai với các yêu cầu cụ thể để thực hiện kiểu như mong đợi một hàm tạo với các đối số nhất định không chung chung và do đó không được phép cho Generics. Quy tắc đó đảm bảo rằng generics thực sự là chung chung –

+1

@Rune FS: Vậy tại sao bạn có thể đặt các hạn chế khác trên chúng như các lớp cơ sở hoặc lớp/structness? – RCIX

+1

@ rama-jka toti: cuối cùng ai đó gọi là thuổng thuổng. Bất cứ ai đến từ nền C++ chỉ được đưa trở lại bằng cách này. – andriej

0

Có lẽ tôi đã đi theo gợi ý từ Tony "jon" ngựa Skeet nhưng có một cách khác để làm điều đó. Vì vậy, chủ yếu cho vui đây là một giải pháp khác nhau (có mặt bên dưới của thất bại trong thời gian chạy nếu bạn quên thực hiện các phương pháp cần thiết nhưng ngược lại không phải cung cấp một phương pháp nhà máy, trình biên dịch sẽ móc bạn một cách kỳ diệu.

public class HomepageCarousel<T> : List<T> where T: IHomepageCarouselItem 
{ 

    private List<T> GetInitialCarouselData() 
    { 
     List<T> carouselItems = new List<T>(); 

     if (jewellerHomepages != null) 
     { 
      foreach (PageData pageData in jewellerHomepages) 
      { 
       T homepageMgmtCarouselItem = null; 
       homepageMgmtCarouselItem = homepageMgmtCarouselItem.create(pageData); 
       carouselItems.Add(homepageMgmtCarouselItem); 
      } 
     } 
     return carouselItems; 
    } 
} 

public static class Factory 
{ 
    someT create(this someT, PageData pageData) 
    { 
     //implement one for each needed type 
    } 

    object create(this IHomepageCarouselItem obj, PageData pageData) 
    { 
     //needed to silence the compiler 
     throw new NotImplementedException(); 
    } 
} 

chỉ để lặp lại "tuyên bố từ chối trách nhiệm" của tôi là rất nhiều lời khuyên để nhắc nhở rằng có thể có những cách tiếp cận khác nhau để giải quyết cùng một vấn đề mà tất cả họ đều rút ra và điểm mạnh. phần đen ma thuật;)

T homepageMgmtCarouselItem = null; 
homepageMgmtCarouselItem = homepageMgmtCarouselItem.create(pageData); 

nhưng bạn tránh hàm tạo perculiar lấy đối số đại biểu. (nhưng tôi thường đi theo phương pháp đó trừ khi tôi đang sử dụng cơ chế tiêm phụ thuộc để cung cấp cho lớp nhà máy đối với tôi. Điều đó vô tình là loại khung DI mà tôi đang làm việc trong thời gian đó; p)

+0

Nếu tôi có quyền này, điều này sẽ yêu cầu viết một khối lượng lớn nếu/else/else/else ... chặn bên trong phương thức Tạo tiện ích mở rộng để xử lý từng loại đúng cách? Vậy thì sử dụng generics là gì? – Groo

+0

Tôi đã thử một cái gì đó tương tự - xem bài chỉnh sửa. Bạn nghĩ sao? – ChrisCa

+0

@Bạn không cần một người lớn nếu có. Bạn sẽ cần một phương pháp mở rộng cụ thể cho từng loại. –

5

Chỉ cần thêm cho các câu trả lời khác:

Điều bạn đang làm ở đây về cơ bản được gọi là chiếu . Bạn có một List của một loại và muốn chiếu từng mục (sử dụng một đại biểu) cho một loại mục khác.

Vì vậy, một chuỗi chung của hoạt động thực sự (sử dụng LINQ):

// get the initial list 
List<PageData> pageDataList = GetJewellerHomepages(); 

// project each item using a delegate 
List<IHomepageCarouselItem> carouselList = 
     pageDataList.Select(t => new ConcreteCarousel(t)); 

Hoặc, nếu bạn đang sử dụng Net 2.0, bạn có thể viết một lớp helper như:

public class Project 
{ 
    public static IEnumerable<Tdest> From<Tsource, Tdest> 
     (IEnumerable<Tsource> source, Func<Tsource, Tdest> projection) 
    { 
     foreach (Tsource item in source) 
      yield return projection(item); 
    } 
} 

và sau đó sử dụng nó như:

// get the initial list 
List<PageData> pageDataList = GetJewellerHomepages(); 

// project each item using a delegate 
List<IHomepageCarouselItem> carouselList = 
     Project.From(pageDataList, 
      delegate (PageData t) { return new ConcreteCarousel(t); }); 

Tôi không chắc phần còn lại của mã trông như thế nào, nhưng tôi tin rằng GetInitialCarouselData không phải là nơi thích hợp để xử lý việc khởi tạo, đặc biệt vì nó về cơ bản nhân đôi chức năng chiếu (đó là khá chung chung và có thể được trích xuất trong một lớp riêng biệt, như Project).

[Chỉnh sửa] Hãy suy nghĩ về những điều sau đây:

Tôi tin rằng ngay bây giờ lớp học của bạn có một constructor như thế này:

public class HomepageCarousel<T> : List<T> 
    where T: IHomepageCarouselItem, new() 
{ 
    private readonly List<PageData> jewellerHomepages; 
    public class HomepageCarousel(List<PageData> jewellerHomepages) 
    { 
     this.jewellerHomepages = jewellerHomepages; 
     this.AddRange(GetInitialCarouselData()); 
    } 

    // ... 
} 

Tôi đoán đây là trường hợp, bởi vì bạn đang truy cập vào một jewellerHomepages lĩnh vực trong phương pháp của bạn (vì vậy tôi đoán bạn đang lưu trữ nó trong ctor).

Có một số vấn đề với cách tiếp cận này.

  • Bạn có tham chiếu đến jewellerHomepages không cần thiết. Danh sách của bạn là một danh sách các IHomepageCarouselItems, vì vậy người dùng có thể chỉ cần gọi phương thức Clear() và điền nó với bất cứ thứ gì họ thích. Sau đó, bạn kết thúc với một tham chiếu đến một cái gì đó bạn không sử dụng.

  • Bạn có thể khắc phục điều đó bằng cách tháo rời sân:

    public class HomepageCarousel(List<PageData> jewellerHomepages) 
    { 
        // do not save the reference to jewellerHomepages 
        this.AddRange(GetInitialCarouselData(jewellerHomepages)); 
    } 
    

    Nhưng điều gì sẽ xảy ra nếu bạn nhận ra rằng bạn có thể muốn khởi tạo nó sử dụng một số lớp khác, khác nhau từ PageData? Ngay bây giờ, bạn đang tạo danh sách như sau:

    HomepageCarousel<ConcreteCarousel> list = 
        new HomepageCarousel<ConcreteCarousel>(listOfPageData); 
    

    Bạn có tự để lại bất kỳ tùy chọn nào để khởi tạo nó với bất kỳ điều gì khác trong một ngày không? Ngay cả khi bạn thêm một hằng số mới, phương thức GetInitialCarouselData của bạn vẫn còn quá cụ thể để chỉ sử dụng PageData làm nguồn.

Kết luận là: Không sử dụng loại cụ thể trong công cụ xây dựng của bạn nếu không cần. Tạo các mục danh sách thực tế (các thể hiện cụ thể) ở một nơi khác.

+0

cảm ơn đề xuất - xem bài chỉnh sửa. Bạn nghĩ sao? – ChrisCa

+0

Tôi nghĩ rằng 'HomepageCarousel ' lớp biết quá nhiều về phần còn lại của thế giới (vi phạm nguyên tắc trách nhiệm duy nhất). Nếu bạn có một số chức năng gắn liền với giao diện ** IHomepageCarouselItem **, thì bạn nên xử lý ** chỉ ** chức năng đó trong lớp của bạn. Việc thực hiện thực tế nên được giao cho người gọi lớp (như được hiển thị trong câu trả lời của Jon), hoặc bạn có thể chỉ cần tạo các cá thể thích hợp ở một nơi khác. Sau khi tất cả, điều này chỉ đơn giản là một danh sách các mục (có lẽ với một số chức năng liên quan đến IHomepageCarouselItem). Tại sao 'Danh sách ' cần bao giờ tạo ra các thể hiện của 'T'? – Groo

+0

Cần tạo các phiên bản T để điền danh sách tức là để làm cho Danh sách có một số dữ liệu trong – ChrisCa

0

Tại sao bạn không đặt phương thức "hàm tạo" tĩnh trên giao diện? Một chút hacky tôi biết, nhưng bạn phải làm những gì bạn phải làm ...

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