简体   繁体   中英

C# more specific version on generic function

I have the following function

public static T Translate<T>(T entity)
{
    ....
}

Now if T is en IEnumerable<> I want to have a different behaviour so I made a second function

public static IEnumerable<T> Translate<T>(IEnumerable<T> entities)
{
    ....
}

When I invoke it like this

IEnumerable<string> test = new List<string>().AsEnumerable();
Translate(test);

However when I invoke it like this

Func<IEnumerable<string>> func = () => new List<string>().AsEnumerable();
Translate(func.Invoke())

It goes to the first one. Why does this happen and what is the best construction to solve this?

UPDATE

I build a new example with the problem

static void Main(string[] args)
{
    Func<IEnumerable<string>> stringFunction = () => new List<string>().AsEnumerable();
    InvokeFunction(ExtendFunction(stringFunction));
}

private static T Convert<T>(T text) where  T : class 
{
    return null;
}

private static IEnumerable<T> Convert<T>(IEnumerable<T> text)
{
    return null;
}

private static Func<T> ExtendFunction<T>(Func<T> func) where T : class 
{
    return () => Convert(func.Invoke());
}

private static T InvokeFunction<T>(Func<T> func)
{
    return func.Invoke();
}

The first function gets invoken now when I expect the second to be invoked.

You need to either add a second overload of ExtendFunction :

private static Func<IEnumerable<T>> ExtendFunction<T> (Func<IEnumerable<T>> func) where T : class
{
    return () => Convert(func.Invoke());
}

Or make the first overload invoke Convert method dynamically:

private static Func<T> ExtendFunction<T> (Func<T> func) where T : class
{
    return () => Convert((dynamic)func.Invoke());
}

The reason is that your ExtendFunction method chooses Convert method at compile time. You can avoid that be either adding a second overload of ExtendFunction which chooses the Convert method you need, or by moving the choice of Convert method to run time.

In C#, the closest to specialization is to use a more-specific overload; however this works only when the type is know at compile time.

In your case the type is decided at run time because of IEnumerable<T> , but the compiler cannot guarantee that it will be IEnumerable<T> . If you add this line in your main method you'll get the second function called. Convert(text: new List<string>().AsEnumerable());

this is because the type is know at compile time

so try your Main in this way and look the differences

static void Main(string[] args)
{
   Func<IEnumerable<string>> stringFunction = () => new List<string>().AsEnumerable();
   InvokeFunction(ExtendFunction(stringFunction));//first function invoked
   Convert(text: new List<string>().AsEnumerable());//second function invoked
}

I had the same problem a few weeks ago. You can solve this by specifying the type explicitely on calling the method but that's not really worth it, because it means everybody using your methods have to know about this fact.

We solved our problem by actually giving the method another name. In your case, that would be a second method named:

public static IEnumerable<T> TranslateAll<T>(IEnumerable<T> entities)

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