c# - 如何使用泛型类型约束来强制添加方法存在
问题描述
我有这个使用通用代码的代码:
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?
解决方案
每当您有一个包含 的通用方法时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();
}
推荐阅读
- reactjs - 如何在reactjs中使用material-ui以红色显示helperText
- python - 定义函数后,如何解决其中一个参数?
- svelte - Svelte build 位于名为 public 的文件夹中
- flutter - 如何使用半径在mapbox_gl颤振中绘制圆
- python - 如何在循环中的某些 xy 值处自动注释点或线
- php - 选择元素选项 HTML 上不显示空白
- tensorflow - 如何将 Tensorflow.js 中的 3D 张量重塑为 4D 张量?
- hex - 1位十六进制最大值
- c++ - 存储稍后在 Node C++ 插件中调用的 JS 回调
- c - 使用全局变量时可以省略'extern'吗?