2012-02-04 20 views
28

Tôi có loại chung Store<T> và sử dụng Activator để tạo một phiên bản loại này. Bây giờ làm thế nào, sau khi sử dụng Activator, tôi có thể bỏ đối tượng kết quả của loại object trở lại loại được khởi tạo không? Tôi biết loại mà tôi đã sử dụng để khởi tạo chung chung. Hãy xem đoạn mã sau:Làm thế nào để sử dụng Activator để tạo ra một thể hiện của một loại chung và đúc nó trở lại loại đó?

class Store<T> where T : IStorable 
{} 

class Beer : IStorable 
{} 

class BeerStore : Store<Beer> 
{} 

Type storeType = someObjectThatImplementsIStorable.GetType(); 
Type classType = typeof(Store<>); 
Type[] typeParams = new Type[] { storeType }; 
Type constructedType = classType.MakeGenericType(typeParams); 

object x = Activator.CreateInstance(constructedType, new object[] { someParameter }); 

Những gì tôi muốn làm là một cái gì đó như thế này:

var store = (Store<typeof(objectThatImplementsIStorable)>)x; 

nhưng điều đó không làm việc vì lý do rõ ràng. Thay vào đó, tôi đã thử:

var store = (Store<IStorable>)x; 

có thể làm việc theo ý kiến ​​của tôi, nhưng cung cấp InvalidCastException.

Làm cách nào để tôi có quyền truy cập lại vào các phương thức Store<T> mà tôi biết nằm trong đối tượng x?

+0

Bạn có thể làm 'lý do obvious' rõ ràng hơn? Hoặc chỉ cần cho chúng tôi biết là vì lý do gì. Hoặc bạn có thực sự có nghĩa là' typeof ... ' – Snowbear

+1

@Snowbear rằng đoạn mã không biên dịch vì Generics không cho phép các cá thể của các kiểu được sử dụng như T. Tôi không biết kiểu đó, nó có thể là bất kỳ đối tượng nào thực hiện – Bazzz

+0

Loại trình gỡ rối nghĩ x là gì? Nó có đúng loại không? – CarneyCode

Trả lời

27

Kể từ khi thực tế loại T có sẵn cho bạn chỉ thông qua phản ánh, bạn sẽ cần phải truy cập vào các phương pháp của Store<T> qua phản ánh cũng như:

Type constructedType = classType.MakeGenericType(typeParams); 

object x = Activator.CreateInstance(constructedType, new object[] { someParameter }); 
var method = constructedType.GetMethod("MyMethodTakingT"); 
var res = method.Invoke(x, new object[] {someObjectThatImplementsStorable}); 

EDIT Bạn cũng có thể xác định một giao diện bổ sung IStore mà không sử dụng Generics, và sử dụng IStorable thay vì:

interface IStore { 
    int CountItems(IStorable item); 
} 
class Store<T> : IStore where T : IStorable { 
    int CountItems(IStorable item) { 
     return count; 
    } 
} 

Store<T> của bạn sẽ vẫn còn chung chung, nhưng bạn sẽ nhận được quyền truy cập vào CountItems của nó bằng cách đúc để IStore:

var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter }); 
var count = x.CountItems((IStorable)someObjectThatImplementsStorable); 
+0

Tôi sợ nó sẽ đi xuống này. Có phải đó là một nguyên tắc nhỏ khi tôi biết loại T thực tế chỉ thông qua sự phản ánh rằng tất cả sự truy cập vào đối tượng thực tế cũng cần phải trải qua sự phản chiếu? – Bazzz

+0

@ Bazzz Có, nếu điều duy nhất bạn biết về 'someObjectThatImplementsIStorable' là nó là một' đối tượng' đang thực hiện 'IStorable', có rất ít bạn có thể làm để tránh sự phản chiếu. Nếu bạn đã nhận được 'someObjectThatImplementsIStorable' như một kiểu generic' T', bạn cũng có thể chuyển sang 'Store '. Một thay thế sẽ là tránh generics trong một tập con của phương pháp (tôi sẽ cập nhật câu trả lời ngay để cho bạn thấy làm thế nào). – dasblinkenlight

+0

Tôi sẽ đi theo cách này, tôi biết tôi có thể thực hiện công việc này và các tùy chọn khác dường như không khả dụng. Cảm ơn bạn đã trả lời. Tôi muốn xem tập hợp con, nếu bạn có thời gian. Chỉ cần quan tâm và học một cái gì đó. – Bazzz

3

Bạn không thể quấn nó?

cái gì đó như

public Store<T> IConstructStore<T>(T item) where T : IStorable 
{ 
return Activator.CreateInstance(typeof(Store<T>), new object[] { someParameter }) as Store<T>; 
} 

hoặc am i thiếu những gì bạn đang cố gắng để làm gì?

IE

class Program 
{ 
    static void Main(string[] args) 
    { 
     Beer b = new Beer(); 
     var beerStore = IConstructStore(b); 
     Console.WriteLine(beerStore.test); 
     Console.WriteLine(beerStore.GetType().ToString()); 
    } 

    public static Store<T> IConstructStore<T>(T item) where T : IStorable 
    { 
     return Activator.CreateInstance(typeof(Store<T>), new object[] { }) as Store<T>; 
    } 
} 

interface IStorable { } 

class Store<T> where T : IStorable 
{ 
    public int test = 1; 
} 

class Beer : IStorable 
{ } 

in

1 
ConsoleApp1.Store'1[ConsoleApp1.Beer] 
+0

thú vị, nhưng sau đó? Làm cách nào để gọi phương thức này? bạn có thể thêm một dòng mã mẫu không? – Bazzz

+0

vâng, miễn là bạn gọi nó với bất kỳ đối tượng nào T được suy ra, tức là bia item = new Beer(); var beerStore = IConstructStore (mục); beerStore.whatever() nên hoạt động tốt –

+0

'typeof (Store )' không biên dịch, không phải như 'Store ' – Bazzz

2

Câu trả lời phù hợp nhất theo ý kiến ​​của tôi là 'bạn không thể làm theo cách này'.

Bạn có thể thử giới thiệu giao diện IStorage và thử biến nó thành biến thể hoặc contravariant (bạn đã thấy tùy chọn đó chưa?). Nếu nó không phải là một lựa chọn, ví dụ nếu bạn có cả hai loại đầu vào và đầu ra chung loại được sử dụng trong Storage, sau đó không có cách nào để thực hiện những gì bạn muốn.Lý do là Storage<Beer> không thể được sử dụng một cách an toàn như Storage<IStorable> do trường hợp này:

Storage<IStorable> store = new Storage<Beer>(); // let's pretend we can do it 
store.Save(new StorableButNotBeer()); // what will happen here? 

Cách giải quyết có thể chỉ cho bạn như tôi thấy là để di chuyển đúc ra từ phương pháp này và đúc các đối tượng trong các nơi mà bạn biết tất cả các loại chính xác:

public void object CreateStore(Type istorableType) 
{ 
    // here is your activator code, but you will have to return an object 
} 

var beerStore = (Store<Beer>)CreateStore(typeof(Beer)); 
+0

Cảm ơn bạn đã đề xuất của bạn, tôi thực sự có các loại chung đầu vào và đầu ra trong Store để đường dẫn không hoạt động. Theo gợi ý khác, bên cạnh một thể hiện của objectThatImplementsIStorable, tôi không biết Type thực sự ở bất cứ đâu. Tôi nghĩ tôi sẽ sử dụng Reflection trên đối tượng mà Activator tạo ra. 1 cho giải thích tốt về lý do tại sao vấn đề tồn tại. – Bazzz

0

Giả sử someObjectThatImplementsIStorable thuộc loại MyStorable.

ví dụ: MyStorable someObjectThatImplementsIStorable = new MyStorable(); ... // phần còn lại của mã của bạn tại đây.

Sau đó, x không thể truyền tới Cửa hàng nhưng có thể truyền tới Cửa hàng. Sau đây sẽ hoạt động: (Store) x

Lưu ý rằng mặc dù MyStorable thực hiện IStorable, không có mối quan hệ giữa Store và Store. Đây là hai lớp riêng biệt không xuất phát từ nhau.

u.

+0

Bạn đã quản lý để mô tả vấn đề nói cách khác. :) – Bazzz

+0

Bạn không thể truyền loại chung mà không bao gồm T, tức là (Store) x không biên dịch –

0

T phải là loại cửa hàng tránh việc sử dụng typeof (Cửa hàng

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