繁体   English   中英

如何使用泛型类型约束来强制 Add 方法存在

[英]How to use generic type constraint to enforce Add method existence

我有这个使用通用代码的代码:

    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() ——您不需要它。

下面是一个示例,您可以通过重写方法来解决这些问题。 这有点棘手,因为您必须使用嵌套函数在async Task使用yield return

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();
}

您可以尝试使用ICollection<string>接口作为泛型类型约束where T : ICollection<string>, new()

没有办法保证在编译时泛型上有特定的方法,但您可以强制泛型类型实现一个接口,例如具有 Add 方法的 IList。

IE

public T Foo<T>() where T : IList, new()
{
  var list = new T();
  list.Add(...);
  return list;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM