我正在编写一个通用代码,该代码应该处理从多个源加载数据的情况。我有一个带有以下签名的方法:
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()
但这太过分了:当我通过TResult
时,我已经知道TContract
和TSection
到底是什么。在我的例子中:
public interface ISourceObserverConfiguration
: IDatabaseConfigurable<SourceObserverContract, SourceObserverSection>
但我必须写下以下内容:
sourceObserverSection.LoadFromAnySource<SourceObserverContract,
SourceObserverSection,
SourceObserverConfiguration>
(_registrationServiceConfiguration.ServiceBaseUri, nodeName);
您可以看到我必须两次指定对<SourceObserverContract, SourceObserverSection>
,这违反了 DRY 原则。所以我想写一些类似的东西:
sourceObserverSection.LoadFromAnySource<SourceObserverConfiguration>
(_registrationServiceConfiguration.ServiceBaseUri, nodeName);
并从界面推断SourceObserverContract
和SourceObserverSection
。
是否可以在 C#中使用,或者我应该手动指定它?
IDatabaseConfigurable
看起来像:
public interface IDatabaseConfigurable<in TContract, in TSection>
where TContract : ConfigContract
where TSection : ConfigurationSection
{
string RemoteName { get; }
void LoadFromContract(TContract contract);
void LoadFromSection(TSection section);
}
然后扩展只是根据一些逻辑调用这两个方法。我必须指定类型,因为我需要访问每个特定实现的属性,所以我需要一个协方差。
答案 0 :(得分:2)
不,你不能。类型推断不考虑方法的返回类型。 TContract
可能包含所需的所有信息,但类型推断不会使用它。
您需要制作方法签名的TResult
部分,以便推断出类型。 IDataBaseConfigurable<TContract, TSection>
是多余的,不需要它是通用的,只需使用{{1}}作为方法的返回类型。
答案 1 :(得分:1)
使用LoadFromAnySource
方法的当前方法签名,这不能像您一样推断出来。但是,这可以通过修改LoadFromAnySource
签名来推断。
由于您已经知道ISourceObserverConfiguration
接口(并且我们知道它重新实现了IDatabaseConfigurable<SourceObserverContract, SourceObserverSection>
接口),因此在方法声明中使用它作为通用约束:
而不是
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()
使用此
public static TResult LoadFromAnySource<TResult>
(this SourceObserverSection section,
string serviceBaseUri,
string nodeName)
where TResult : ISourceObserverConfiguration, new()
这消除了对TContract
和TSection
的需求,因为它们在ISourceObserverConfiguration
界面中已知。编译器知道接口约束是IDatabaseConfigurable<SourceObserverContract, SourceObserverSection>
,它只会起作用。
此外,由于这是一种扩展方法,我们在ISourceObserverConfiguration
上定义了一般约束,因此我们需要扩展SourceObserverSection
。
sourceObserverSection.LoadFromAnySource<SourceObserverConfiguration>
(_registrationServiceConfiguration.ServiceBaseUri, nodeName);
<强>更新强>
根据 OP 对问题的修改/澄清,我有以下内容:
是否可以在 C#中使用,或者我应该手动指定它?
您应该手动指定它。根据重新实现的要求推断出这一点, 换句话说,由于您有IDatabaseConfigurable
的多个实现,调用者必须通过其TContract
和TSection
约束来指定要使用的实现。
答案 2 :(得分:1)
这取决于您的代码有多灵活,以及您使用它做了什么。通常,不 - 您需要指定 all 泛型类型,或者不指定它们。
这意味着简单地传递TResult
并不意味着其他泛型类型被解析(即使在逻辑上,它们也可以)。
根据您可以更改定义的程度,可以更加整洁:
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 { }
让你写下:
var sect = new ConfigurationSection();
sect.LoadFromAnySource<DatabaseConfigurable<SourceObserverContract, SourceObserverSection>>("a", "B");
不同之处在于您将约束放在IDatabaseConfigurable
上,而不是放在方法上。您还需要使接口协变。如果您的设计无法做到这一点,那么就我所见,我无法做您想要完成的事情(没有非通用的IDatabaseConfigurable
)