2016-05-06 17 views
10

Tôi đang viết mã chung để xử lý các tình huống khi dữ liệu được tải từ nhiều nguồn. Tôi có một phương pháp có chữ ký sau đây:Đơn giản hóa loại chung loại suy ra

public static TResult LoadFromAnySource<TContract, TSection, TResult> 
    (this TSection section, 
      string serviceBaseUri, 
      string nodeName) 
    where TSection : ConfigurationSection 
    where TResult : IDatabaseConfigurable<TContract, TSection>, new() 
    where TContract : new() 

Nhưng nó là một quá mức cần thiết: khi tôi vượt qua TResult, tôi đã biết những gì TContractTSection chính xác là. Trong ví dụ của tôi:

public interface ISourceObserverConfiguration 
    : IDatabaseConfigurable<SourceObserverContract, SourceObserverSection> 

Nhưng tôi phải viết như sau:

sourceObserverSection.LoadFromAnySource<SourceObserverContract, 
             SourceObserverSection, 
             SourceObserverConfiguration> 
    (_registrationServiceConfiguration.ServiceBaseUri, nodeName); 

Bạn có thể thấy rằng tôi phải chỉ rõ cặp <SourceObserverContract, SourceObserverSection> hai lần, đó là một vi phạm DRY Principe. Vì vậy, tôi muốn viết một cái gì đó như:

sourceObserverSection.LoadFromAnySource<SourceObserverConfiguration> 
    (_registrationServiceConfiguration.ServiceBaseUri, nodeName); 

và làm SourceObserverContractSourceObserverSection suy ra từ giao diện.

Có thể trong C# hoặc tôi nên chỉ định ở mọi nơi theo cách thủ công?

IDatabaseConfigurable trông giống như:

public interface IDatabaseConfigurable<in TContract, in TSection> 
    where TContract : ConfigContract 
    where TSection : ConfigurationSection 
{ 
    string RemoteName { get; } 

    void LoadFromContract(TContract contract); 

    void LoadFromSection(TSection section); 
} 

Sau đó mở rộng chỉ gọi hai phương pháp này dựa trên một số logic. Tôi phải xác định các loại bởi vì tôi cần phải truy cập các thuộc tính của từng thực hiện cụ thể, vì vậy tôi cần một hiệp phương sai.

+1

Nếu chữ ký của phương thức là 'IDatabaseConfigurable LoadFromAnySource (phần TSection này, chuỗi serviceBaseUri, string nodeName, Func contractCreator) '(hoặc chỉ' hợp đồng TContract'), các kiểu có thể được suy ra từ cách sử dụng. –

+1

Vi phạm nguyên tắc DRY như thế nào? Đó là một chút phức tạp hơn "không viết cùng một điều hai lần". Bạn có thể không quan tâm rằng bạn viết 't' ba lần trong' twitter', phải không? : D Cả hai đều * không * giống nhau - một là tổng quát hơn cái kia. Trong mọi trường hợp, nhận xét của @JeroenMostert là khá nhiều cách tiếp cận tốt nhất, và hoàn toàn nên là một câu trả lời. Mặc dù có lẽ ở đâu đó giống như Lập trình viên và không phải SO - Tôi không nghĩ đó là một câu hỏi hay cho SO, thực sự. – Luaan

+0

@JeroenMostert Có, nhưng họ sẽ không biết 'IDatabaseConfigurable' nào được sử dụng. – Rob

Trả lời

2

Không, bạn không thể. Kiểu suy luận không tính đến kiểu trả về của một phương thức. TResult có thể chứa tất cả thông tin cần thiết nhưng suy luận loại sẽ không sử dụng nó.

Bạn cần phải thực hiện TContract một phần chữ ký của phương thức để loại có thể được suy ra. TResult là không cần thiết, không cần thiết phải là chung chung, chỉ cần sử dụng IDataBaseConfigurable<TContract, TSection> làm kiểu trả về của phương thức.

+0

Đây là cuộc thảo luận về giải pháp có thể có: https://github.com/dotnet/roslyn/issues/5023 –

1

Với chữ ký phương thức hiện tại của phương pháp LoadFromAnySource, điều này không thể suy ra như bạn muốn. Tuy nhiên, điều này có thể được suy ra với một sửa đổi đối với chữ ký LoadFromAnySource.

Vì bạn đã biết giao diện ISourceObserverConfiguration (và từ này, chúng tôi biết rằng nó lại thực hiện các giao diện IDatabaseConfigurable<SourceObserverContract, SourceObserverSection>), sử dụng như là một hạn chế chung thay vì trong tờ khai phương pháp của bạn:

Thay vì

public static TResult LoadFromAnySource<TContract, TSection, TResult> 
    (this TSection section, 
      string serviceBaseUri, 
      string nodeName) 
    where TSection : ConfigurationSection 
    where TResult : IDatabaseConfigurable<TContract, TSection>, new() 
    where TContract : new() 

Sử dụng này

public static TResult LoadFromAnySource<TResult> 
    (this SourceObserverSection section, 
      string serviceBaseUri, 
      string nodeName) 
    where TResult : ISourceObserverConfiguration, new() 

Điều này sẽ loại bỏ nhu cầu cho TContractTSection vì chúng được biết đến trong giao diện ISourceObserverConfiguration. Trình biên dịch biết rằng ràng buộc giao diện là IDatabaseConfigurable<SourceObserverContract, SourceObserverSection> và nó sẽ hoạt động.

Ngoài ra, vì đây là phương pháp tiện ích và chúng tôi đang xác định ràng buộc chung trên ISourceObserverConfiguration, chúng tôi cần mở rộng SourceObserverSection.


Sau đó, bạn có thể tiêu thụ nó chính xác như bạn mong muốn:

sourceObserverSection.LoadFromAnySource<SourceObserverConfiguration> 
    (_registrationServiceConfiguration.ServiceBaseUri, nodeName); 

Cập nhật

Dựa trên sửa đổi các OP của/làm rõ cho câu hỏi, tôi đã điều sau đây:

Có thể trong C# hoặc tôi nên chỉ định ở mọi nơi theo cách thủ công?

Bạn nên chỉ định thủ công. Đó là không phải có thể với suy ra điều này dựa trên yêu cầu của việc tái triển khai nơi giao diện cơ sở xác định loại bê tông mà ràng buộc cấp cao nhất của bạn đang cố giải quyết. Nói cách khác, vì bạn có nhiều triển khai của IDatabaseConfigurable, người gọi phải chỉ định thực hiện nào để sử dụng thông qua các ràng buộc TContractTSection của mình.

+0

Điều đó sẽ không biên dịch vì bạn không định nghĩa 'TSection' nữa. – juharr

+0

Tôi cập nhật câu trả lời của mình và sẽ đánh giá cao một upvote, vì nó bây giờ là chính xác –

+0

'ISourceObserverConfiguration' là một trong những giao diện cụ thể, tôi có nhiều giao diện, kế thừa' IDatabaseConfigurable'. Vì vậy, không may là tôi không thể viết 'ISourceObserverConfiguration' ở đây, nó phải chung chung hơn. –

1

Loại tùy thuộc vào độ linh hoạt của mã của bạn và bạn làm gì với mã đó. Nói chung, không - bạn cần phải chỉ định tất cả các loại chung chung hoặc không có loại nào trong số đó.

Điều này có nghĩa là chỉ cần đi qua TResult không có nghĩa là các loại chung khác được giải quyết (mặc dù logic, chúng có thể).

Tùy thuộc vào bao nhiêu bạn có thể thay đổi định nghĩa của bạn, bạn thể làm một chút ngăn nắp:

public static class Helper 
{ 
    public static TResult LoadFromAnySource<TResult>(this ConfigurationSection section, string serviceBaseUri, string nodeName) 
     where TResult : IDatabaseConfigurable<object, ConfigurationSection>, new() 
    { 
     return default(TResult); 
    } 
} 

public class ConfigurationSection { } 
public interface IDatabaseConfigurable<out TContract, out TSection> 
    where TContract : new() 
    where TSection : ConfigurationSection 
{ 
} 

public class DatabaseConfigurable<TContract, TSection> : IDatabaseConfigurable<TContract, TSection> 
    where TContract : new() 
    where TSection : ConfigurationSection 
{ 
} 

public class SourceObserverContract { } 
public class SourceObserverSection : ConfigurationSection { } 

Cho phép bạn viết:

var sect = new ConfigurationSection(); 
sect.LoadFromAnySource<DatabaseConfigurable<SourceObserverContract, SourceObserverSection>>("a", "B"); 

Sự khác biệt là bạn đặt các hạn chế về các IDatabaseConfigurable, thay vì trên phương pháp. Bạn cũng cần phải thực hiện giao diện covariant. Nếu điều đó không thể thực hiện được với thiết kế của bạn, thì bạn không thể thực hiện được những gì bạn đang cố gắng thực hiện (mà không cần phải thực hiện một số không IDatabaseConfigurable)

+0

Tôi biết, nhưng đôi khi nó có thể tránh được bằng cách trích xuất một số loại ở cấp lớp. Ví dụ 'Foo.Bar (B b, C c)' khi chúng ta có thể suy ra 'B' và' C' làm cho chúng ta xác định tất cả các đối số, nhưng 'Foo .Bar (B b, C c)' có thể tránh nó . Nhưng tôi không thấy làm thế nào tôi có thể làm cho cùng một (hoặc tương tự) lừa ở đây ... –

+0

Tôi upvote nó nhưng không may tôi không thể sử dụng nó bởi vì callee sẽ nhận được một kết quả cuối cùng như là một loại chính xác. Nó có thể đúc nó, tất nhiên, nhưng tốt hơn khi hiệp phương sai làm mọi thứ cho chúng ta. –

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