doktron
doktron

Reputation: 103

method return generic func c#

Is it possible to return a generic Func from a method? What I want to do is something like the GetSortFunc below.

public class Example
{
    private List<MyObject> _objects;
    public Example()
    {
        _objects = new List<MyObject>
                        {
                            new MyObject {Id = 1, Name = "First", Value = 100.0},
                            new MyObject {Id = 2, Name = "Second", Value = 49.99},
                            new MyObject {Id = 3, Name = "Third", Value = 149.99}
                        };
    }

    public void Sort(SomeEnum sortOptions)
    {
        _objects = _objects.OrderBy(GetSortFunc(sortOptions));
    }

    private Func<MyObject, TKey> GetSortFunc(SomeEnum sortOptions)
    {
        switch (sortOptions)
        {
            case SomeEnum.First:
                return x => x.Id;
            case SomeEnum.Second:
                return x => x.Name;
            case SomeEnum.Third:
                return x => x.Value;
        }
    }
}

The SomeEnum and MyObject looks like this:

public enum SomeEnum
{
    First,
    Second,
    Third
}

public class MyObject
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Value { get; set; }
}

Is this possible to do or am I on the wrong track?

Upvotes: 2

Views: 699

Answers (3)

usr
usr

Reputation: 171206

The problem is that the return type will vary with the type of TKey. Also, the type parameters of OrderBy will vary. I suggest you just duplicate the OrderBy calls:

IEnumerable<MyObject> ApplySortOrder(IEnumerable<MyObject> items, SomeEnum sortOptions)
{
    switch (sortOptions)
    {
        case SomeEnum.First:
            return items.OrderBy(x => x.Id);
        case SomeEnum.Second:
            return items.OrderBy(x => x.Name);
        case SomeEnum.Third:
            return items.OrderBy(x => x.Value);
    }
}

Alternatively, make GetSortFunc return delegate and call OrderBy dynamically:

private Delegate GetSortFunc(SomeEnum sortOptions)
{
    switch (sortOptions)
    {
        case SomeEnum.First:
            return new Func<MyObject, int>(x => x.Id);
        case SomeEnum.Second:
            return new Func<MyObject, string>(x => x.Name);
        case SomeEnum.Third:
            return new Func<MyObject, int>(x => x.Value);
    }
}

//...

Enumerable.OrderBy(_objects, (dynamic)GetSortFunc(sortOptions));

This would pick the right overload at runtime.

Upvotes: 3

Grundy
Grundy

Reputation: 13381

Simplest way fix your function change it like

private Func<MyObject, object> GetSortFunc(SomeEnum sortOptions)
{
    switch (sortOptions)
    {
        case SomeEnum.First:
            return x => x.Id;
        case SomeEnum.Second:
            return x => x.Name;
        case SomeEnum.Third:
            return x => x.Value;
        default:
            return x => x.Id;
    }
}

Upvotes: 2

JaredPar
JaredPar

Reputation: 755179

A generic Func, or any other generic type, can only be returned from context which has a declared generic parameter. This generic parameter needs to exist on either the method or one of the containing types of the method. In this case there is no generic parameter hence the code cannot function

This is really not a great example of a function that should be generic though. If it were generic it would need to look something like this

private Func<MyObject, TKey> GetSortFunc<TKey>(SomeEnum sortOptions) 
{
    switch (sortOptions)
    {
        case SomeEnum.First:
            return x => (TKey)(object)x.Id;
        case SomeEnum.Second:
            return x => (TKey)(object)x.Name;
        case SomeEnum.Third:
            return x => (TKey)(object)x.Value;
    }
}

All that ugly casting exists because the C# compiler can't find an existing conversion between int and string and the TKey parameter (because it could be literally any type). Also it won't work unless the type of the property matches TKey which is generally a sign of code that can't be generic.

Upvotes: 2

Related Questions