简体   繁体   中英

How can i create a c# method that will return the same collection type as as argument with a different element type?

essentially, exactly as in this question , but in c#.

I want to have something like this:

public IList<double> DoSomething(IList<string> strings) {
    //something
}

But i'd rather have it typed such that the return value is known ahead of time so that i don't have to cast or anything outside the function. How do i do this?

Is there a generic way,

public ListType<double> DoSomething<ListType>(ListType<string> strings) where ListType : ???{

to set this up?

C# does not allow exactly what you want, but the closest thing would be:

public TList2 DoSomething<TList1, TList2>(TList1 strings) 
    where TList1 : IEnumerable<string>
    where TList2 : ICollection<decimal>, new()
{
    var result = new TList2();
    foreach (var s in strings)
    {
        result.Add(decimal.Parse(s));
    }
    return result;
}

and an example:

var input = new HashSet<string>(new[] { "1", "2", "3"});
List<decimal> output = DoSomething<HashSet<string>, List<decimal>>(input);

You can use Linq to do this.

For instance if you just want to parse to double you would do:

List<double> result = strings.Select(double.Parse).ToList()

And you can send in any other method instead of double.Parse:

List<double> result = strings.Select(DoSomethingWithOneItem).ToList()

double DoSomethingWithOneItem(string item) {
  //your conversion logic
}

Unfortunately there is no relation between for instance IList<TInput> and IList<TOutput> that can be used to help here, so you will need to specify both the input and output list type, which becomes a bit cumbersome in the general form:

IList<string> strings  = new List<string> {"1.1", "2.2", "3.3"};
IList<decimal> result = strings.ConvertToSameListType((Func<string, decimal>)decimal.Parse, () => new List<decimal>());

public static class EnumerableExtensioncGeneralVersion
{
    public static TOutputList ConvertToSameListType<TInputList, TOutputList, TInput, TOutput>(this TInputList source, Func<TInput, TOutput> itemConversion, Func<TOutputList> outputListConstructor)
        where TInputList : IEnumerable<TInput>
        where TOutputList : ICollection<TOutput>
    {
        TOutputList result = outputListConstructor();
        foreach (TOutput convertedItem in source.Select(itemConversion))
        {
            result.Add(convertedItem);
        }
        return result;
    }
}

Though you can make it much nicer to use your conversions if you just specify one extension method for each of your favourite collection types that you want to support:

//Seting up inputs
IList<string> strings  = new List<string> {"1.1", "2.2", "3.3"};
IEnumerable<string> enumerableStrings = strings.Select(x => x);
ObservableCollection<string> observableStrings = new ObservableCollection<string>(strings);

//Converting
IList<decimal> resultList = strings.Convert(decimal.Parse);
IEnumerable<decimal> resultEnumerable = enumerableStrings.Convert(decimal.Parse);
ObservableCollection<decimal> observableResult = observableStrings.Convert(decimal.Parse);

public static class EnumerableExtensions
{
    public static IList<TOutput> Convert<TInput, TOutput>(this IList<TInput> source, Func<TInput, TOutput> itemConversion)
    {
        return source.Select(itemConversion).ToList();
    }
    public static IEnumerable<TOutput> Convert<TInput, TOutput>(this IEnumerable<TInput> source, Func<TInput, TOutput> itemConversion)
    {
        return source.Select(itemConversion);
    }
    public static ObservableCollection<TOutput> Convert<TInput, TOutput>(this ObservableCollection<TInput> source, Func<TInput, TOutput> itemConversion)
    {
        return new ObservableCollection<TOutput>(source.Select(itemConversion));
    }
}

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