C # late binding method overloading doesn't work when overloading is defined in a derived class

I need to call the method overload according to the type of the object at runtime using the C # binding functions. It works great when all overloads are defined in the same class as the call. But when overload is defined in a derived class, it will not be bound at run time.

class BaseT {} class DerivedA : BaseT {} class DerivedB : BaseT {} class Generator { public void Generate(IEnumerable<BaseT> objects) { string str = ""; foreach (dynamic item in objects) { str = str + this.Generate(item); //throws an exception on second item } } protected virtual string Generate(DerivedA a) { return " A "; } } class DerivedGenertor : Generator { protected virtual string Generate(DerivedB b) { return " B "; } } class Program { static void Main(string[] args) { List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedB()}; var generator = new DerivedGenertor(); generator.Generate(items); } } 

Here is another more striking example:

 class BaseT {} class DerivedA : BaseT {} class DerivedB : BaseT {} class DerivedC : BaseT { } class Generator { public void Generate(IEnumerable<BaseT> objects) { string str = ""; foreach (dynamic item in objects) { str = str + this.Generate(item); //throws an exception on third item } } public virtual string Generate(DerivedA a) { return " A "; } public virtual string Generate(DerivedC c) { return " C "; } } class DerivedGenertor : Generator { public virtual string Generate(DerivedB b) { return " B "; } } class Program { static void Main(string[] args) { List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedC(), new DerivedB()}; dynamic generator = new DerivedGenertor(); generator.Generate(items); } } 
+5
source share
2 answers

You need to declare Generator just as dynamically so that you have dynamic resolution on the input object and on the called method. But you will have to change the access modifiers to public or protected internal in order to do this, because now you have a method allowed from the outside.

 class BaseT { } class DerivedA : BaseT { } class DerivedB : BaseT { } class Generator { public string Generate(IEnumerable<BaseT> objects) { string str = ""; dynamic generator = this; foreach (dynamic item in objects) { str = str + generator.Generate(item); } return str; } protected internal virtual string Generate(DerivedA a) { return " A "; } } class DerivedGenertor : Generator { protected internal virtual string Generate(DerivedB b) { return " B "; } } class Program { static void Main(string[] args) { List<BaseT> items = new List<BaseT>() { new DerivedA(), new DerivedB() }; var generator = new DerivedGenertor(); string ret = generator.Generate(items); } } 
+3
source

How do you expect him to be connected? The compiler associates the call to this.Generate(item) with the only possible candidate: Generator.Generate(DerivedA a) . This has nothing to do with when the binding occurs; DerivedGenerator.Generate(DerivedB b) not considered a valid candidate because Generator has absolutely no idea about its existence, and you call the method through a statically typed instance of Generator this (I note that the protected method is not a question, even if it was public , the second call failed). Why should the base class know anything about the new virtual method defined in the derived class?

To do this, you either define virtual Generate(DerivedB) in the Generator base class, or override it in DerivedGenerator , or if this is not an option, you make everything resolved at runtime.

In your case, as Brian correctly points out, you will need to make an instance of the Generator dynamic to enable the binding of the Generate call to DerivedGenerator.Generate when it is approved. Otherwise, the selection of candidates will be limited only to those of which the Generator knows.

This obliges you to significantly rebuild your code, since you will also need to make Generate at least internal or internal protected .

In addition, I should note that making a dynamic.Generate(dynamic) call to create a job seems like a big smell to me, and you are probably abusing a system like C #. I would consider reorganizing your code into a more secure solution.

I also recommend that you read Eric Lippert's science fiction series: Wizards and warriors explaining how certain hierarchies of objects cannot be well expressed by the C # system and how you could (but usually shouldn't) use dynamic to achieve double sending to C # to get around some of its limitations.

+2
source

All Articles