2009-02-09 28 views
14

Với giao diện này huyền diệu:Trong C# 4.0 tại sao không thể tham số ngoài trong một phương thức là biến thể?

public interface IHat<out TRabbit> 
{ 
    TRabbit Take(); 
} 

Và hệ thống phân cấp lớp này:

public class Rabbit { } 

public class WhiteRabbit : Rabbit { } 

bây giờ tôi có thể biên dịch này:

IHat<WhiteRabbit> hat1 = null; 
IHat<Rabbit> hat2 = hat1; 

Đó là tuyệt vời. Nhưng nếu tôi xác định giao diện khác nhau:

public interface IHat<out TRabbit> 
{ 
    bool Take(out TRabbit r); 
} 

tôi chỉ ra rằng chiếc mũ có thể trống rỗng, sử dụng một giá trị trả về boolean riêng biệt (các phiên bản trước sẽ có lẽ đã trở về một con thỏ null từ một chiếc mũ trống). Nhưng tôi vẫn chỉ xuất hiện một con thỏ, do đó, không làm bất cứ điều gì một cách hợp lý khác với phiên bản trước.

Trình biên dịch C# 4.0 trong CTP đưa ra một lỗi trong định nghĩa giao diện - nó yêu cầu tham số phương thức 'out' là một kiểu bất biến. Có một lý do cứng nhắc và nhanh chóng tại sao điều này không được phép hay là một thứ có thể được giải quyết trong một phiên bản tương lai?

Trả lời

9

Thú vị. Tuy nhiên, ở cấp CLI không có thứ như "out" - chỉ "ref"; có một thuộc tính giúp các trình biên dịch (để phân định rõ ràng) cho biết "bạn không cần truyền nó trong".

Có thể hạn chế này là do CLI không có "out", chỉ "ref".

+0

Để biết thông tin, tôi đã tìm thấy nhiều blog, v.v ... nhưng không có nhận xét nào là từ nguồn MS chính thức. Tôi khá tự tin nó là chính xác, mặc dù ... phương sai C# 4.0 vẫn dựa trên các quy tắc CLI. –

+0

Nghe có vẻ đủ rồi! –

0

Mặc dù đó là một chút rắc rối, bạn có thể sử dụng một wrapper hiệp phương sai:

public class CovariantListWrapper<TOut, TIn> : IList<TOut> where TIn : TOut 
{ 
    IList<TIn> list; 

    public CovariantListWrapper(IList<TIn> list) 
    { 
     this.list = list; 
    } 

    public int IndexOf(TOut item) 
    { 
     // (not covariant but permitted) 
     return item is TIn ? list.IndexOf((TIn)item) : -1; 
    } 

    public TOut this[int index] 
    { 
     get { return list[index]; } 
     set { throw new InvalidOperationException(); } 
    } 

    public bool Contains(TOut item) 
    { 
     // (not covariant but permitted) 
     return item is TIn && list.Contains((TIn)item); 
    } 

    public void CopyTo(TOut[] array, int arrayIndex) 
    { 
     foreach (TOut t in this) 
      array[arrayIndex++] = t; 
    } 

    public int Count { get { return list.Count; } } 

    public bool IsReadOnly { get { return true; } } 

    public IEnumerator<TOut> GetEnumerator() 
    { 
     foreach (TIn t in list) 
      yield return t; 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public void Insert(int index, TOut item) { throw new InvalidOperationException(); } 
    public void RemoveAt(int index) { throw new InvalidOperationException(); } 
    public void Add(TOut item) { throw new InvalidOperationException(); } 
    public void Clear() { throw new InvalidOperationException(); } 
    public bool Remove(TOut item) { throw new InvalidOperationException(); } 
} 

này cho phép bạn giữ bộ sưu tập như nó đã được ban đầu được đánh máy và tham khảo nó covariantly mà không cần tạo một bản sao tách ra, do đó cập nhật cho bản gốc được nhìn thấy trong việc sử dụng biến thể. Ví dụ:

class CovarianceWrapperExample 
{ 
    class Person { } 
    class Employee : Person { } 

    void ProcessPeople(IList<Person> people) { /* ... */ } 

    void Foo() 
    { 
     List<Employee> employees = new List<Employee>(); 

     // cannot do: 
     ProcessPeople(employees); 

     // can do: 
     ProcessPeople(new CovariantListWrapper<Person, Employee>(employees)); 
    } 
} 
Các vấn đề liên quan