我有一个重载方法-第一个实现总是返回单个对象,第二个实现总是返回一个枚举。
我想使方法泛化和重载,并限制编译器在泛型类型可枚举时试图绑定到非枚举方法.
class Cache
{
T GetOrAdd<T> (string cachekey, Func<T> fnGetItem)
where T : {is not IEnumerable}
{
}
T[] GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem)
{
}
}用在..。
{
// The compile should choose the 1st overload
var customer = Cache.GetOrAdd("FirstCustomer", () => context.Customers.First());
// The compile should choose the 2nd overload
var customers = Cache.GetOrAdd("AllCustomers", () => context.Customers.ToArray());
}这只是我在这里侵犯的简单的坏代码味道,还是可以消除上述方法的歧义,这样编译器就可以始终正确地调用代码?
增加对除“重命名其中一种方法”以外的任何答案的人的选票。
发布于 2010-10-08 14:15:27
只使用一种方法,并让它动态地检测IEnumerable<T>情况,而不是通过泛型约束尝试不可能的情况。根据要存储/检索的对象是否是可枚举的对象,必须处理两个不同的缓存方法,这将是“代码嗅觉”。而且,仅仅因为它实现了IEnumerable<T>并不意味着它一定是一个集合。
发布于 2010-10-08 14:14:50
重命名其中一个方法。您会注意到,List<T>有一个Add和AddRange方法;遵循这个模式。对一项做某事和对一组项做某事在逻辑上是不同的任务,因此使方法具有不同的名称。
发布于 2010-10-08 14:31:28
--这是一个很难支持的用例,因为C#编译器如何执行重载解析,以及它如何决定要绑定到哪个方法。。
第一个问题是方法的约束不是签名的一部分。,不会考虑过载解决。
您需要克服的第二个问题是编译器从可用的签名中选择最佳匹配--在处理泛型时,这通常意味着SomeMethod<T>(T)将被认为是比SomeMethod<T>( IEnumerable<T> )更好的匹配。特别是当您有像T[]或List<T>这样的参数时。
但更根本的是,您必须考虑对单个值与值集合的操作是否真的是相同的operation.,如果它们在逻辑上是不同的,那么您可能只想使用不同的名称来澄清。也许有一些用例可以证明,单个对象和对象集合之间的语义差异是没有意义的.但在这种情况下,为什么要实现两种不同的方法呢?目前尚不清楚方法重载是否是表达差异的最佳方式。让我们来看看一个导致混乱的例子:
Cache.GetOrAdd("abc", () => context.Customers.Frobble() );首先,请注意,在上面的示例中,我们选择忽略返回参数。其次,请注意,我们在Frobble()集合上调用了一些方法Customers。现在你能告诉我哪种超负荷的GetOrAdd()会被调用吗?显然,不知道Frobble()返回的类型是不可能的。就我个人而言,我认为,在可能的情况下,应该避免从语法中轻易推断出其语义的代码。如果我们选择更好的名字,这个问题就会得到缓解:
Cache.Add( "abc", () => context.Customers.Frobble() );
Cache.AddRange( "xyz", () => context.Customers.Frobble() );最终只有三个选项可以消除示例中方法的歧义:
IEnumerable<T>。选项1是不言自明的,所以我将不再谈论它。
选项2也很容易理解:
var customers = Cache.GetOrAdd("All",
() => (IEnumerable<Customer>)context.Customers.ToArray());选项3更复杂。让我们来看看我们可以实现它的方法。
方法是通过更改Func<>委托的签名来实现,例如:
T GetOrAdd<T> (string cachekey, Func<object,T> fnGetItem)
T[] GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem)
// now we can do:
var customer = Cache.GetOrAdd("First", _ => context.Customers.First());
var customers = Cache.GetOrAdd("All", () => context.Customers.ToArray());就我个人而言,我发现这个选项非常丑陋,不直观,而且令人困惑。引入未使用的参数是很糟糕的..。但遗憾的是,它会奏效的。
更改签名(稍微不那么糟糕)的另一种方法是将返回值设置为out参数:
void GetOrAdd<T> (string cachekey, Func<object,T> fnGetItem, out T);
void GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem, out T[])
// now we can write:
Customer customer;
Cache.GetOrAdd("First", _ => context.Customers.First(), out customer);
Customer[] customers;
var customers = Cache.GetOrAdd("All",
() => context.Customers.ToArray(), out customers);但这真的更好吗?它阻止我们使用这些方法作为其他方法调用的参数。海事组织,它还使守则不那么明确,也更不容易理解。
最后一种选择是向方法中添加另一个泛型参数,该参数标识返回值的类型:
T GetOrAdd<T> (string cachekey, Func<T> fnGetItem);
R[] GetOrAdd<T,R> (string cachekey, Func<IEnumerable<T>> fnGetItem);
// now we can do:
var customer = Cache.GetOrAdd("First", _ => context.Customers.First());
var customers = Cache.GetOrAdd<Customer,Customer>("All", () => context.Customers.ToArray());因此可以使用提示来帮助编译器为我们选择重载.好的。但是,看看我们作为开发人员必须做的所有额外工作(更别提引入的丑陋和错误机会)了。真的值得吗?特别是当一种简单可靠的技术(将方法命名不同)已经存在来帮助我们的时候?
https://stackoverflow.com/questions/3891157
复制相似问题