Tôi đã gặp một vấn đề biên dịch ngày hôm nay khiến tôi bối rối. Hãy xem xét hai lớp container này.Generics, inheritance, và phương thức giải quyết thất bại của trình biên dịch C#
public class BaseContainer<T> : IEnumerable<T>
{
public void DoStuff(T item) { throw new NotImplementedException(); }
public IEnumerator<T> GetEnumerator() { }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { }
}
public class Container<T> : BaseContainer<T>
{
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
Các định nghĩa cựu DoStuff(T item)
và quá tải sau nó với DoStuff <Tother>(IEnumerable<Tother>)
đặc biệt để có được xung quanh sự vắng mặt của covariance/contravariance của C# (cho đến 4 Tôi nghe).
Mã này
Container<string> c = new Container<string>();
c.DoStuff("Hello World");
lượt truy cập một lỗi biên dịch khá lạ. Lưu ý sự vắng mặt của <char>
từ cuộc gọi phương thức.
Loại 'char' không thể được sử dụng làm thông số loại 'Tother' trong loại hoặc phương thức chung 'Container.DoStuff (System.Collections.Generic.IEnumerable)'. Không có chuyển đổi quyền anh từ 'char' thành 'string'.
Về cơ bản, trình biên dịch đang cố gắng để mứt kêu gọi của Mẹ để DoStuff(string)
vào Container.DoStuff<char>(IEnumerable<char>)
vì string
cụ IEnumerable<char>
, thay vì sử dụng BaseContainer.DoStuff(string)
.
Cách duy nhất tôi đã tìm thấy để làm biên dịch này là thêm DoStuff(T)
đến lớp được thừa kế
public class Container<T> : BaseContainer<T>
{
public new void DoStuff(T item) { base.DoStuff(item); }
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
Tại sao trình biên dịch cố gắng giơ một chuỗi như IEnumerable<char>
khi 1) nó biết nó có thể' t (với sự hiện diện của một lỗi biên dịch) và 2) nó có một phương thức trong lớp cơ sở biên dịch tốt? Tôi có hiểu nhầm điều gì đó về generics hoặc phương thức ảo trong C# không? Có một bản sửa lỗi nào khác ngoài việc thêm một số new DoStuff(T item)
vào số Container
không?
Tôi đồng ý này có vẻ lạ, nhưng nó là đúng theo spec. Đây là kết quả của sự tương tác của hai quy tắc: (1) kiểm tra khả năng ứng dụng độ phân giải quá tải xảy ra TRƯỚC KHI kiểm tra ràng buộc, và (2) các phương pháp áp dụng trong các lớp dẫn xuất là ALWAYS tốt hơn các phương thức áp dụng trong các lớp cơ sở. Cả hai đều là các quy tắc hợp lý hợp lý; chúng chỉ xảy ra tương tác đặc biệt nặng trong trường hợp của bạn. –
Để biết chi tiết, xem phần 7.5.5.1, cụ thể là các bit có nội dung: (1) "Nếu phương pháp tốt nhất là phương pháp chung, các đối số kiểu (được cung cấp hoặc suy luận) được kiểm tra với các ràng buộc ..." và (2) " tập hợp các phương pháp ứng cử viên được giảm xuống để chỉ chứa các phương thức từ các kiểu xuất phát nhất ... " –
Cuối cùng vấn đề của bạn ở đây là một vấn đề thiết kế. Bạn đang quá tải một phương thức "DoStuff" để có nghĩa là cả hai "làm công cụ cho một giá trị duy nhất của loại T", và "làm công cụ để một chuỗi các giá trị của loại T". Điều này chạy vào các vấn đề nghiêm trọng "có chủ ý giải quyết" theo nhiều cách - ví dụ, khi "loại T" là một chuỗi. Bạn sẽ thấy rằng các lớp sưu tập hiện có trong BCL đã được thiết kế cẩn thận để tránh vấn đề này; các phương thức lấy một mục được gọi là "Frob", các phương thức lấy một chuỗi các mục được gọi là "FrobRange", ví dụ "Thêm" và "AddRange" trên danh sách. –