Tôi đang làm việc để viết lại giao diện thông thạo của mình cho thư viện lớp IoC, và khi tôi tái cấu trúc một số mã để chia sẻ một số chức năng phổ biến thông qua một lớp cơ sở, tôi nhấn vào một snag.Suy luận kiểu chung một phần có thể có trong C#?
Note: Đây là điều mà tôi muốn để làm, không phải cái gì tôi có để làm. Nếu tôi phải làm với một cú pháp khác, tôi sẽ, nhưng nếu có ai có ý tưởng về cách làm cho mã của tôi biên dịch theo cách tôi muốn, nó sẽ được chào đón nhiều nhất.
Tôi muốn một số phương thức mở rộng có sẵn cho một lớp cơ sở cụ thể và các phương thức này phải chung chung, với một kiểu chung, liên quan đến một đối số cho phương thức, nhưng các phương pháp cũng phải trả về một kiểu cụ thể liên quan đến con cháu cụ thể mà chúng được gọi.
Tốt hơn với ví dụ mã so với mô tả ở trên.
Dưới đây là một ví dụ đơn giản và đầy đủ về những gì không công việc:
using System;
namespace ConsoleApplication16
{
public class ParameterizedRegistrationBase { }
public class ConcreteTypeRegistration : ParameterizedRegistrationBase
{
public void SomethingConcrete() { }
}
public class DelegateRegistration : ParameterizedRegistrationBase
{
public void SomethingDelegated() { }
}
public static class Extensions
{
public static ParameterizedRegistrationBase Parameter<T>(
this ParameterizedRegistrationBase p, string name, T value)
{
return p;
}
}
class Program
{
static void Main(string[] args)
{
ConcreteTypeRegistration ct = new ConcreteTypeRegistration();
ct
.Parameter<int>("age", 20)
.SomethingConcrete(); // <-- this is not available
DelegateRegistration del = new DelegateRegistration();
del
.Parameter<int>("age", 20)
.SomethingDelegated(); // <-- neither is this
}
}
}
Nếu bạn biên dịch này, bạn sẽ nhận được:
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingConcrete' and no extension method 'SomethingConcrete'...
'ConsoleApplication16.ParameterizedRegistrationBase' does not contain a definition for 'SomethingDelegated' and no extension method 'SomethingDelegated'...
Những gì tôi muốn là dành cho phần mở rộng phương pháp (Parameter<T>
) để có thể được gọi trên cả hai ConcreteTypeRegistration
và DelegateRegistration
và trong cả hai trường hợp, loại trả về phải khớp với loại tiện ích được gọi.
Vấn đề là như sau:
Tôi muốn viết:
ct.Parameter<string>("name", "Lasse")
^------^
notice only one generic argument
mà còn Parameter<T>
trả về một đối tượng cùng loại nó đã được gọi về, có nghĩa là:
ct.Parameter<string>("name", "Lasse").SomethingConcrete();
^ ^-------+-------^
| |
+---------------------------------------------+
.SomethingConcrete comes from the object in "ct"
which in this case is of type ConcreteTypeRegistration
Có cách nào tôi có thể lừa trình biên dịch tạo ra bước nhảy vọt này cho tôi không?
Nếu tôi thêm hai đối số kiểu chung chung cho phương thức Parameter
, suy luận kiểu buộc tôi để thể cung cấp cả hai, hoặc không, có nghĩa là điều này:
public static TReg Parameter<TReg, T>(
this TReg p, string name, T value)
where TReg : ParameterizedRegistrationBase
mang lại cho tôi điều này:
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
Using the generic method 'ConsoleApplication16.Extensions.Parameter<TReg,T>(TReg, string, T)' requires 2 type arguments
Mà chỉ là xấu.
Tôi có thể dễ dàng sắp xếp lại các lớp, hoặc thậm chí làm cho các phương thức không mở rộng phương pháp bằng cách giới thiệu chúng vào hệ thống phân cấp, nhưng câu hỏi của tôi là nếu tôi có thể tránh phải sao chép các phương thức cho hai hậu duệ. chỉ khai báo chúng một lần cho lớp cơ sở.
Hãy để tôi nói lại điều đó. Có cách nào để thay đổi các lớp trong ví dụ mã đầu tiên ở trên, sao cho cú pháp trong phương pháp chính có thể được giữ lại mà không sao chép các phương thức được đề cập?
Mã sẽ phải tương thích với cả C# 3.0 và 4.0.
Sửa: Lý do tôi không muốn rời cả hai đối số kiểu chung chung để suy luận là đối với một số dịch vụ, tôi muốn chỉ định một giá trị tham số cho một tham số nhà xây dựng mà là của một loại chuyền, tuy nhiên trong một giá trị là hậu duệ. Đối với thời điểm này, việc kết hợp các giá trị đối số được chỉ định và hàm tạo chính xác để gọi được thực hiện bằng cả tên và loại đối số.
Hãy để tôi đưa ra một ví dụ:
ServiceContainerBuilder.Register<ISomeService>(r => r
.From(f => f.ConcreteType<FileService>(ct => ct
.Parameter<Stream>("source", new FileStream(...)))));
^--+---^ ^---+----^
| |
| +- has to be a descendant of Stream
|
+- has to match constructor of FileService
Nếu tôi rời cả hai để gõ suy luận, loại thông số sẽ FileStream
, không Stream
.
Vâng, trông giống như giải pháp tốt nhất. Tôi đang thử nghiệm một trong những nơi mà tôi đã giới thiệu generics vào lớp cơ sở là tốt, và chỉ cần thả phương pháp mở rộng, nhưng ví dụ của bạn cho phép tôi giữ chúng như là phương pháp mở rộng, mà tôi ủng hộ trong trường hợp này. Tức là, hai phương thức mở rộng chỉ gọi một phương thức phổ biến là chỉ định đúng loại. –