C# 模板选项

C# Template options

我正在尝试做一些可以在 C++ 中工作的东西,但 C# 对我来说有点挑战。

我有一个方法可以执行一些解析、数据库访问、web access/etc 并最终得到一系列要添加到容器中的字符串。有时我需要将它添加到哈希集或列表等。

所以,在 C++ 中,这看起来像这样:

<template T>
bool GetStrings(T& container)
{
    ...
    std::string foo = "bar";
    ...
    container.add(foo);  // This statement is within a loop and if checks
    ...
    return true;
}

我在 C# 中尝试过:

private bool GetStrings<T>(ref T cont)
{
    string foo = "BAR";
    cont.Add(foo); // T does not contain a definition for Add ...
    return true;
}

一位同事建议改用容器的基础 class/interface。所以,我尝试了这个(在看到 List、Hashset 等有一个共同的接口定义之后):

private bool GetStrings(ref ICollection<string> cont)
{
    string foo = "BAR";
    cont.Add(foo);
    return true;
}

我希望能够像这样调用此方法:

HashSet<string> a = new HashSet<string>();
List<string> b = new List<string>();
// etc other classes containing "Add" method
if (GetString(ref a)) ...  // These aren't all in one place, but spread out
if (GetString(ref b)) ...  // and the types are based on what is useful in
if (GetString(ref c)) ...  // each particular context.
if (GetString(ref d)) ...

现在方法本身可以编译,但我无法调用它。我得到 Best overload has invalid arguments, "Argument 1: cannot convert from 'ref System.Collections.Generic.List' to 'ref System.Collections.Generic.ICollection'"

我认为它只是一个需要类型转换的东西。所以,我试试 :

if (GetString(ref (ICollection<string>)a)) ...

然后我得到"A ref or out argument must be an assignable variable"。所以,问题是……这可以在 C# 中完成吗,我是不是完全走错了路?我还研究了传递 Object ref 并尝试调用 'GetType' 和 'GetMethod' 等来确定 Add 是否可用等等,但无法弄清楚如何调用 Add 方法。

您需要对您的方法使用通用约束:

private bool GetStrings<T>(T cont) where T : ICollection<string>
{
    string foo = "BAR";
    // The compiler knows that T is always ICollection<string>, and can infer that Add
    // is a valid method call
    cont.Add(foo);
    return true;
}

此外,您不需要通过引用传递列表 - 它已经是一个引用。用 C++ 的说法,这就像你传递一个指向那里的指针的指针。

使用generic type constraints。 您可以执行以下编译并运行的操作:

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();

        HashSet<string> a = new HashSet<string>();
        List<string> b = new List<string>();

        if (t.GetString(ref a))
        {

        }
        if (t.GetString(ref b))
        {

        }
    }

    public class Test
    {
        public bool GetString<T>(ref T cont) where T : ICollection<string>
        {
            string foo = "BAR";
            cont.Add(foo);
            return true;
        }
    }
}

顺便说一句,所有的集合都已经是引用类型了,除非你想改变原来的变量,ref是不需要的。参见 here for an explanation