Using generic types in overloaded methods

I have a general method:

public bool DoSomething<T>(T item) where T: IBase { return DoSomethingSpecific(item); } public bool DoSomethingSpecific(IBase item) { return true; } public bool DoSomethingSpecific(IAdvanced item) { return false; } 

Note that the IAdvanced interface outputs / inherits the IBase interface.

I found that if I call DoSomething where the element is of type IAdvanced, it always always returns false. I do not understand this. I know that since IAdvanced is an IBase type (since it is a child of this interface), this can cause confusion between the two overloaded types of the DoSomethingSpecific method. However, as I understand it with my limited knowledge of C #, the IAdvanced method should be chosen here. This is an example of how I came to this conclusion:

 public class Advanced: IAdvanced { public void CallMethod() { DoSomething(this); } } 

This leads to a true meaning.

However, if I do this:

 public class Advanced: IAdvanced { public void CallMethod() { DoSomethingSpecific(this); } } 

It returns false, which is what I expect.

I have to say that I have not used generics before. I tried, though, but I am always fixated on such a case, and then I don’t completely see the point of using generics (in addition to data structures such as trees and linked lists).

This time I decided to come here for advice. Is there a clear problem with what I'm trying to do? Maybe it makes no sense to try to do what I'm busy here?

+4
source share
3 answers

As far as he knows, this is IBase. The compiler must decide which method you are calling, and why it always chooses this one.

A dirty trick would be for this:

 public static bool DoSomething<T>(T item) where T: IBase { var isAdvanced = typeof(IAdvanced).IsAssignableFrom(typeof(T)); return isAdvanced ? DoSomethingSpecific((IAdvanced)item) : DoSomethingSpecific(item); } 

Another way is to use the Double-Dispatch / Visitor pattern:

 public interface IDoSomethingVisitor { bool DoSomethingSpecific(IBase base); bool DoSomethingSpecific(IAdvanced adv); } 

The DoSomething method will be in your IBase interface:

 public interface IBase{ void DoSomething(IDoSomethingVisitor visitor); } 

And in your implementations:

 public class Base : IBase { public bool DoSomething(IDoSomethingVisitor visitor) { visitor.DoSomething(this); } } public class Advanced : IAdvanced { public bool DoSomething(IDoSomethingVisitor visitor) { visitor.DoSomething(this); } } 

In this case, the problem is solved using pure inheritance. The actual instance is the one that allows which method to be called. No, if.

+1
source

I cannot reproduce this, which means something else is happening. I suspect that you really wanted to say that he always returns the truth. Here is what I see here:

 using System; public interface IBase {} public interface IAdvanced : IBase {} public class Base : IBase {} public class Advanced : IAdvanced {} class Test { public static bool DoSomething<T>(T item) where T: IBase { return DoSomethingSpecific(item); } public static bool DoSomethingSpecific(IBase item) { return true; } public static bool DoSomethingSpecific(IAdvanced item) { return false; } static void Main() { Console.WriteLine(DoSomething(new Base())); // True Console.WriteLine(DoSomething(new Advanced())); // True } } 

Now you also write:

I know that since IAdvanced is an IBase type (since it is a child of this interface), this can cause confusion between the two overloaded types of the DoSomethingSpecific method. However, as I understand it with my limited knowledge of C #, the IAdvanced method should be chosen here.

Of course, then you should expect false to return, while I expect true be returned.

You see that compiling this method determines the resolution of the overload inside DoSomething<T> . This choice is made once - not once for every other T Therefore, if you look in the compiled IL for DoSomething<T> , you will only see a call to DoSomethingSpecific(IBase) , never DoSomethingSpecific(IAdvanced) . There is no polymorphism.

As for how you can β€œfix” this, you will need to explain in more detail what you really want to achieve. In particular, what would you like if you had this code:

 IBase x = new AdvancedImplementation(); DoSomething(x); 

Here T will be IBase , but the value will refer to the implementation of IAdvanced . If you want DoSomethingSpecific(IAdvanced) executed in this case, and if you use C # 4, then you can use dynamic typing:

 public static bool DoSomething(IBase item) { dynamic dynamicItem = item; // Dispatch dynamically based on execution-time type return DoSomethingSpecific(dynamicItem); } 

Note that the method should no longer be general; it will not do any good.

If, on the other hand, you want to decide what to call based on T , you will need to use something like ivowiblo's solution.

Personally, I will try to avoid both of these decisions - I usually think that such a thing is a design symptom that can be improved in other ways.

+4
source

I would reset the compile time check and run it:

 public bool DoSomething(IBase item) { var itemAdvanced = item as IAdvanced; if (itemAdvanced != null) return DoSomethingSpecific(itemAdvanced); else return DoSomethingSpecific(item); } 

The reason is that if your caller statically knows the object as IBase , but is IBase at run IAdvanced , would it not be preferable to treat it as IAdvanced ? This is probably also the fastest way to do what you want. I would only go for the dynamic approach, if you see the need, for example. because there can be many different methods.

+2
source

Source: https://habr.com/ru/post/1414635/


All Articles