简体   繁体   中英

C# Template options

I'm trying to do something which would work in C++, but C# is a bit of a challenge for me.

I have a method which does a bit of parsing, database access, web access/etc and eventually ends up with a series of strings to add to a container. Sometimes I need to add it to a hashset or list or etc.

So, in c++, this would look like this :

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

I tried that in C# :

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

A coworker suggested using the container's base class/interface instead. So, I tried this out (after seeing List, Hashset, etc have a common interface definition) :

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

I want to be able call this method like this :

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)) ...

Now the method itself compiles, but I can't invoke it. I get Best overload has invalid arguments, "Argument 1: cannot convert from 'ref System.Collections.Generic.List' to 'ref System.Collections.Generic.ICollection'"

I figure its just a type cast needed thing. So, I try :

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

Then I get "A ref or out argument must be an assignable variable" . So, the question is... can this be done in C#, am I just totally on the wrong track? I also looked into passing an Object ref and trying to call 'GetType' and 'GetMethod' and such to figure out if Add was available and such but couldn't figure out how to call the Add method.

Use generic type constraints . You can do the following that compiles and runs:

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

By the way all collections are already reference types, unless you want to change the original variable, the ref is not necessary. See here for an explanation .

You need to use a generic constraint on your method:

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

Additionally, you don't need to pass your list by reference - it's already a reference. In C++ parlance, it's like you're passing a pointer to a pointer there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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