简体   繁体   中英

Generics and Functions in C#

Here is some code, it won't compile but essentially want I want to create is a function that parses as CSV file and then converts the values in the CSV list to a specific type.

    Func<string, Func<string,T>, IEnumerable<T>> parser =(string csv, Func<string, T> newFunc) =>
    {
        List<T> items = new List<T>();
        string[] ary = csv.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string val in ary)
        {
            try
            {
                items.Add(newFunc(val));
            }
            catch { }
        }
        return items;
    }

I want this function to be generic so T is the type that I want the CSV list to be converted to. The usage of this function would be something like:

 string csvList ="1,2,3";
        IEnumerable<int> list = parser(csvList, (string val) => { return Convert.ToInt32(val)});

However this obviously won't work because I haven't defined T. So is it possible to define T in a similar manner to generic methods like so:

    Func<T><string, Func<string,T>, IEnumerable<T>> parser =(string csv, Func<string, T> newFunc) =>
    {
        List<T> items = new List<T>();
        string[] ary = csv.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string val in ary)
        {
            try
            {
                items.Add(newFunc(val));
            }
            catch { }
        }
        return items;
    }

And then use this like so:

string csvList ="1,2,3";
        IEnumerable<int> list = parser<int>(csvList, (string val) => { return Convert.ToInt32(val)});

Is there a way of doing something like this in C#?

-- Further Edit

Thanks to those of you have respond, in the code I have written I actually use a method like you have described, but I was wondering just a general aside if it was possible to do this as Func without the need for a method call.

Your function signature for parser is syntactically incorrect and should be defined as follows.

Func<string, Func<string,T>, IEnumerable<T>>
    parser<T>(string csv, Func<string, T> newFunc)
{
    // implementation
}

Note, that when defining a generic function, you need to specify the generic parameters after the function name, and before the parameter list (see parser<T> above).

Maybe I'm missing something but I think this is what you need:

public IEnumerable<T> Parse<T>(string csv, Func<string, T> func)
{
   foreach(var item in csv.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries))
   {
      yield return func(item);
   }
}

From the usage I would say you need a functuion that returns an IEnumerable<> , not a Func<> .

Which would simplify the whole thing to:

    IEnumerable<T> parser<T>(string csv, Func<string, T> newFunc)
    {
        List<T> items = new List<T>();

        string[] ary = csv.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
        foreach (string val in ary)
        {
            try
            {
                items.Add(newFunc(val));
            }
            catch
            {
                // empty catch alert
            }
        }
        return items;
    }

And your usecase is missing a semicolon (after ToInt32() ) :

    string csvList ="1,2,3";
    IEnumerable<int> list = 
      parser<int>(csvList, (val) => { return Convert.ToInt32(val); });

Edit, thanks to BFree: if you don't need the exceptionhandling to be inside parser (and I hope that empty catch is there only in the sample code), you can eliminate the List<T> items and use yield return instead.

Try this out. I think this accomplishes what you're trying to do without a lot of hocus pocus.

string csvList = "1,2,3";
IEnumerable<int> list = parser<int>(csvList, Convert.ToInt32);



static IEnumerable<T> parser<T>(string csv, Converter<string, T> converter)
{
    string[] array = csv.Split(new string[] { "," }, 
                        StringSplitOptions.RemoveEmptyEntries);

    T[] items = Array.ConvertAll(array, converter);

    return items;
}

Update: If you want a single line, you could do it this way:

string csvList = "1,2,3";
var list = csvList.Split(new string[] { "," }, 
           StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x));

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