我有使用通用代码的以下代码:
public static async Task<List<string>> GetDomainsAsync(IMemoryCache _cache)
{
return await ContextCache.CacheTryGetValueSet<List<String>>("SP_GET_DOMAINS", _cache);
}
public static async Task<Dictionary<String, String>> GetSettingsAsync(IMemoryCache _cache)
{
return await ContextCache.CacheTryGetValueSet<Dictionary<String, String>>("SP_GET_SETTINGS", _cache);
}
这是通用方法:
private static async Task<T> CacheTryGetValueSet<T>(String SPName, IMemoryCache _cache) where T : new()
{
dynamic data = new T();
....
while (reader.Read())
{
if (reader.FieldCount == 1)
{
data.Add(reader.GetString(0));
}
else if (reader.FieldCount == 2)
{
data.Add(reader.GetString(0), reader.GetString(1));
}
}
....
return data;
}
如何确保T
实际上具有Add
方法?
可以添加什么通用类型约束来确保只能传递IEnumerable?
答案 0 :(得分:3)
只要您有一个包含if (typeof(T))
的通用方法,您就可能做错了。泛型的全部要点是它们在各种类型上的作用完全相同。如果类型太不同(例如,一个字典需要两个字段而一个列表需要一个字段),则最终会以通用方法编写非通用代码,这只会使事情变得混乱。
在这种情况下,您可能应该有两种方法,一种可以接受一个类型参数(返回一个IEnumerable<TItem>
,调用者可以轻松地将其转换为List或在LINQ语句中使用),另一个可以接受两种类型的方法。参数(返回IEnumerable<KeyValuePair<TKey,TValue>>
,调用者可以将其转换为字典或在LINQ中使用)。
此外,当行可用时,您可能应该使用yield return
,这样您就不必在调用方开始处理数据之前就读取整个行集。这样可以使您的表现更流畅,并避免出现弄清楚是否存在Add()
方法的问题,而您不必使用它。
以下是您可以重写方法来解决这些问题的方法的示例。这有点棘手,因为您必须使用嵌套函数才能在yield return
中使用async Task
:
public async Task<IEnumerable<TItem>> CacheTryGetValueSet<TItem>(string storedProcedureName, IMemoryCache cache)
{
IEnumerable<TItem> Enumerate(SqlDataReader source)
{
while (source.Read())
{
yield return source.GetFieldValue<TItem>(0);
}
}
var reader = await OpenReaderAsync(storedProcedureName);
if (reader.FieldCount != 1) throw new ArgumentException("That type of cache doesn't return a single column.");
return Enumerate(reader);
}
public async Task<IEnumerable<KeyValuePair<TKey,TValue>>> CacheTryGetValueSet<TKey,TValue>(string storedProcedureName, IMemoryCache cache)
{
IEnumerable<KeyValuePair<TKey,TValue>> Enumerate(SqlDataReader source)
{
while (source.Read())
{
yield return new KeyValuePair<TKey, TValue>
(
source.GetFieldValue<TKey>(0),
source.GetFieldValue<TValue>(1)
);
}
}
var reader = await OpenReaderAsync(storedProcedureName);
if (reader.FieldCount != 2) throw new ArgumentException("That type of cache doesn't return two columns!");
return Enumerate(reader);
}
现在,呼叫者可以通过以下方式调用它:
public static async Task<List<string>> GetDomainsAsync(IMemoryCache _cache)
{
return await ContextCache.CacheTryGetValueSet<string>("SP_GET_DOMAINS", _cache).ToList();
}
public static async Task<Dictionary<String, String>> GetSettingsAsync(IMemoryCache _cache)
{
return await ContextCache.CacheTryGetValueSet<String, String>("SP_GET_SETTINGS", _cache).ToDictionary();
}
答案 1 :(得分:1)
您可以尝试将ICollection<string>
接口用作通用类型约束where T : ICollection<string>, new()
。
答案 2 :(得分:1)
无法保证在编译时泛型上有特定的方法,但是您可以强制泛型实现一个具有Add方法的接口,例如IList。
IE。
public T Foo<T>() where T : IList, new()
{
var list = new T();
list.Add(...);
return list;
}