2010-05-03 30 views
6

Tôi có một chức năng chung được giới hạn trong cấu trúc. Đầu vào của tôi được đóng hộp ("đối tượng"). Có thể unbox giá trị tại thời gian chạy để tránh phải kiểm tra từng loại có thể và làm các phôi bằng tay?Việc mở hộp chung kiểu giá trị đóng hộp

Xem ví dụ trên:

public struct MyStruct 
    { 
     public int Value; 
    } 

    public void Foo<T>(T test) 
     where T : struct 
    { 
     // do stuff 
    } 

    public void TestFunc() 
    { 
     object o = new MyStruct() { Value = 100 }; // o is always a value type 

     Foo(o); 
    } 

Trong ví dụ này, tôi biết rằng o phải là một struct (tuy nhiên, nó không cần phải được MyStruct ...). Có cách nào để gọi Foo mà không có tấn mã boilerplate để kiểm tra cho mọi loại struct có thể?

Cảm ơn bạn.

+0

Câu hỏi thú vị. Thông tin là tất cả ở đó nhưng có vẻ như không phải là một cách để "nói chung đúc để bắt nguồn". Rất bực bội. Ai đó đã đề cập đến động lực (cần 4.5). Và tôi tự hỏi liệu người ta có thể xây dựng thứ gì đó với cây biểu cảm không. –

Trả lời

7

.NET Generics được triển khai theo cách cho phép các loại giá trị dưới dạng thông số loại chung mà không phải chịu bất kỳ chi phí boxing/unboxing nào. Bởi vì bạn đang đúc để đối tượng trước khi gọi Foo bạn không tận dụng lợi thế đó, trên thực tế bạn thậm chí không tận dụng lợi thế của generics ở tất cả.

Toàn bộ điểm sử dụng generics ở nơi đầu tiên là thay thế "thành ngữ đối tượng". Tôi nghĩ rằng bạn đang thiếu khái niệm ở đây. Bất kỳ loại nào T xảy ra, nó có sẵn tại thời gian chạy và vì bạn đã hạn chế nó thành cấu trúc được đảm bảo là loại cấu trúc.

TestFunc của bạn có thể được viết như thế này mà không có vấn đề:

public void TestFunc() 
{ 
    MyStruct o = new MyStruct() { Value = 100 }; // o is always a value type 

    Foo<MyStruct>(o); 
} 

Nhìn vào Foo, nó sẽ trông như thế này trong ví dụ của bạn:

public void Foo<T>(T test) 
    where T : struct 
{ 
    T copy = test; // T == MyStruct 
} 

EDIT:

Ok, vì OP làm rõ những gì ông muốn gọi phương pháp chung chung nhưng không biết loại cấu trúc của mình (nó chỉ là đối tượng). Cách dễ nhất để gọi phương thức chung của bạn với tham số kiểu đúng là sử dụng một chút phản ánh.

public void TestFunc() 
{ 
    object o = new DateTime(); 

    MethodInfo method = this.GetType().GetMethod("Foo"); 
    MethodInfo generic = method.MakeGenericMethod(o.GetType()); 
    generic.Invoke(this, new object[] {o}); 


} 
public void Foo<T>(T test) 
    where T : struct 
{ 
    T copy = test; // T == DateTime 
} 
+0

Nhưng nếu tôi không biết rằng o sẽ là một MyStruct? Nó có thể bởi bất kỳ cấu trúc hoặc loại giá trị? TestFunc chỉ là một ví dụ, trong ứng dụng của tôi dữ liệu đến từ một từ điển , nơi giá trị có thể bởi bất cứ điều gì. Tuy nhiên, tôi biết rằng nó phải là một cấu trúc và do đó tôi muốn gọi Foo mà không kiểm tra loại bê tông. – slurmomatic

+0

Nhưng tại sao bạn cần loại bê tông? Điểm của việc sử dụng generics là trừu tượng ra khỏi loại bê tông. Bạn có cần đúc một vật thể vào loại bê tông hay không? Sau đó, bạn cần phải xem xét việc truyền theo ví dụ. –

+0

Tôi không cần loại bê tông, nhưng vì tôi không thể chuyển sang "struct", tôi cần biết loại bê tông để gọi Foo (). – slurmomatic

2

Không; bạn đang sử dụng object, nghĩa là (theo định nghĩa) không phải là loại cấu trúc/giá trị. Tại sao bạn cố ý đấm bốc giá trị theo cách này?

+1

Tất cả các giá trị của tôi được lưu trữ trong Từ điển , nếu tôi có thể có từ điển Tôi sẽ rất vui khi sử dụng :) Tôi không cố tình "đánh giá giá trị trong ứng dụng thực sự của tôi" chỉ là một ví dụ đơn giản. – slurmomatic

+2

@sluromatic: Tôi có thể thấy khó khăn, sau đó. Thật không may, không có cách nào để làm những gì bạn đang cố gắng làm mà không biết loại giá trị cụ thể thực tế. –

+0

Không, bạn có thể sử dụng phép thuật phản chiếu nhỏ (xem câu trả lời của tôi bên dưới). –

0

Toàn bộ điểm sử dụng Generics là tránh những tình huống như thế này.

Khi bạn thực sự "đóng" chung chung với một loại cấu trúc, bạn loại bỏ sự cần thiết phải kiểm tra kiểu thời gian chạy: tức là.

Foo<MyStruct>(MyStruct test); 

Triển khai Foo của bạn, có thể giả định rằng nó đang xử lý cấu trúc.

+0

Có, nhưng tôi không biết đó là một MyStruct, tôi chỉ biết rằng đó là một cấu trúc. Nó có thể là MyStruct2, MyStruct3, int, char, v.v. – slurmomatic

+0

Chính xác, trong những trường hợp đó bạn thực hiện cuộc gọi như Foo (thử nghiệm MyStruct2) Foo (thử nghiệm MyStruct3) v.v ... Tôi không hoàn toàn chắc chắn tại sao bạn 'd sử dụng generics chỉ để làm việc xung quanh tính năng chính của họ (thiết kế kiểu tham số thời gian) – Pierreten

+0

Ok, ví dụ. Tôi có một lớp chung Giá trị nhưng cũng là một giao diện IValue (so sánh với Danh sách và IList). Tôi biết rằng tất cả các IValues ​​của tôi là một số phiên bản cụ thể của Value . Nhưng để gọi bất kỳ phương pháp nào có giá trị tôi phải truyền đến loại bê tông, ví dụ: Giá trị , Giá trị v.v. – slurmomatic

0

(Được đánh dấu là CW vì bạn không thể chuyển trường hợp ValueType đến yêu cầu chung là struct nhưng có thể hữu ích cho những người khác gặp phải câu hỏi này).


Thay vì tuyên bố o như một object, bạn có thể sử dụng một loại System.ValueType, mà chỉ có thể được gán struct giá trị; bạn không thể lưu trữ object trong số ValueType.

Tuy nhiên, tôi thành thật không chắc chắn liệu điều đó có làm bất kỳ điều gì về (un) quyền anh hay không. Lưu ý rằng ECMA-334 11.1.1 nói:

System.ValueType không phải là một loại giá trị. Thay vào đó, nó là một loại lớp mà từ đó tất cả các loại giá trị được tự động bắt nguồn.

+0

Bất kỳ trường hợp nào của 'ValueType' là trớ trêu thay, một tham chiếu. Điều này sẽ không làm việc cho generics bị hạn chế với các loại giá trị. –

0

Tôi không biết chính xác những gì bạn đang cố gắng để archieve, nhưng bạn có thể vượt qua một đại biểu/lambda để Unbox giá trị, và chọn một số giá trị trong struct bạn quan tâm:

(Cập nhật ngày đoạn mã này sau khi bình luận slurmomatics)

public void Foo<TValue>(object test, Func<object, TValue> ValueSelector) 
      where TValue : struct 
{ 
    TValue value = ValueSelector(test); 

    // do stuff with 'value' 
} 

public void TestFunc() 
{ 
    object o = new MyStruct() { Value = 100 }; 

    // Do the unboxing in the lambda. 
    // Additionally you can also select some 
    // value, if you need to, like in this example 
    Foo(o, x => ((MyStruct)x).Value); 
} 

cập nhật:

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

public static void Foo<TUnboxed>(object test) 
        where TUnboxed : struct 
{ 
    try 
    { 
     TUnboxed unboxed = (TUnboxed)test; 
    } 
    catch (InvalidCastException ex) 
    { 
     // handle the exception or re-throw it... 
     throw ex; 
    } 

    // do stuff with 'unboxed' 
} 

public void TestFunc() 
{ 
    // box an int 
    object o = 100; 

    // Now call foo, letting it unbox the int. 
    // Note that the generic type can not be infered 
    // but has to be explicitly given, and has to match the 
    // boxed type, or throws an `InvalidCastException` 
    Foo<int>(o); 
} 
+0

Hm, không. Tôi vẫn còn có cùng một vấn đề mà tôi có một đối tượng o, không phải là một MyStruct o. Tôi biết rằng có phải là một cấu trúc tại thời gian chạy nhưng tôi không biết loại bê tông tại thời gian biên dịch. – slurmomatic

+0

@slurmomatic: ok, đã cập nhật câu trả lời của tôi –

+0

Ok, cảm ơn. Tuy nhiên, vấn đề vẫn còn. Tôi không biết đó là một int (hoặc MyStruct vv), tôi chỉ biết đó là một loại giá trị (struct). – slurmomatic

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