C # add-in overload, does it work?

The presence of a class that has a method, for example:

class Window { public void Display(Button button) { // ... } } 

Is it possible to overload the method with another, wider one, as follows:

 class WindowExtensions { public void Display(this Window window, object o) { Button button = BlahBlah(o); window.Display(button); } } 

What happened when I tried is that I have infinite recursion. Is there any way to make this work? I want the extension method to be called only when another method cannot be called.

+6
extension-methods
source share
5 answers

Go to the specification. First, we need to understand the rules for method calls. Roughly speaking, you start with the type specified by the instance to which you are trying to call the method. You are approaching the chain of inheritance by looking for an accessible method. Then you follow your inference rules and overload rules and call the method if that succeeds. Only if no such method is found, try to treat the method as an extension method. Therefore, from §7.5.5.2 (calls to the extension method), in particular, see the Excerpt:

In a method call (section 7.5.5.1) of one of the forms

expr.identifier()

expr.identifier(args)

expr.identifier<typeargs>()

expr.identifier<typeargs>(args)

if normal call processing does not find applicable methods, an attempt is made to process the construct as a call to the extension method .

The rules that go beyond this are a little complicated, but for the simple case that you presented to us, it is quite simple. If there is no applicable instance method, then the extension method WindowExtensions.Display(Window, object) will be activated. The instance method is applicable if the Window.Display parameter is a button or implicitly attached to the button. Otherwise, the extension method will be called (because everything that comes from object is implicitly discarded to object ).

So, if there is no important bit that you are missing, then what you are trying to do will work.

So, consider the following example:

 class Button { } class Window { public void Display(Button button) { Console.WriteLine("Window.Button"); } } class NotAButtonButCanBeCastedToAButton { public static implicit operator Button( NotAButtonButCanBeCastedToAButton nab ) { return new Button(); } } class NotAButtonButMustBeCastedToAButton { public static explicit operator Button( NotAButtonButMustBeCastedToAButton nab ) { return new Button(); } } static class WindowExtensions { public static void Display(this Window window, object o) { Console.WriteLine("WindowExtensions.Button: {0}", o.ToString()); Button button = BlahBlah(o); window.Display(button); } public static Button BlahBlah(object o) { return new Button(); } } class Program { static void Main(string[] args) { Window w = new Window(); object o = new object(); w.Display(o); // extension int i = 17; w.Display(i); // extension string s = "Hello, world!"; w.Display(s); // extension Button b = new Button(); w.Display(b); // instance var nab = new NotAButtonButCanBeCastedToAButton(); w.Display(b); // implicit cast so instance var nabexplict = new NotAButtonButMustBeCastedToAButton(); w.Display(nabexplict); // only explicit cast so extension w.Display((Button)nabexplict); // explictly casted so instance } } 

Will open

 WindowExtensions.Button: System.Object Window.Button WindowExtensions.Button: 17 Window.Button WindowExtensions.Button: Hello, world! Window.Button Window.Button Window.Button WindowExtensions.Button: NotAButtonButMustBeCastedToAButton Window.Button Window.Button 

on the console.

+9
source share

Perhaps, although you should be careful with overload options, it is generally recommended to avoid object types, as this often causes confusing code. You might be mistaken in the funny way that C # chooses overloads. He will choose a “closer” correspondence with types that can be indirectly applied to the “next” that has exact matches (see this question ).

 Button myButton = // get button Window currentWindow = // get window // which method is called here? currentWindow.Display( myButton ); 

You want your code to be clear enough, especially when returning this code after a year or so, which causes overloading.

Extension methods provide a truly elegant way to extend the functionality of objects. You can add behavior that was not originally there. You should be careful with them, although they are very prone to creating confusing code. It is best to avoid the names of methods that have already been used, even if they are explicit overloads, since they do not appear after the class in intellisense.

The problem here is that the extension method can implicitly convert your button into an object and therefore selects itself as the best match, rather than the actual display method. You can explicitly call the extension method as a regular static call, but you cannot force it to call the base class method.

I would change the name of the extension method:

 object somethingToMakeIntoAButton = // get object Window currentWindow = // get window // which method is called here? currentWindow.DisplayButton( somethingToMakeIntoAButton ); 

Then...

 class WindowExtensions { public void DisplayButton(this Window window, object o) { Button button = BlahBlah(o); // now it clear to the C# compiler and human readers // that you want the instance method window.Display(button); } } 

Alternatively, if the second parameter of the extension method was a type that could not be implicitly converted to a Button (say, int or string ), this confusion also did not occur.

+2
source share

This should work, the compiler will almost always choose an instance method with an acceptable signature by the exact signature extension method.

According to this article :

An instance method with an acceptable signature using an expanding conversion will almost always be preferable to an extension method with an exact signature. If this leads to binding to the instance method, when you really want to use the extension method, you can explicitly call the extension method using a common way to invoke the agreement. It is also a way to eliminate the ambiguity of the two methods when they are not more specific.

Are you sure you are explicitly passing the button?

Or void Display(Button button) call itself recursively?

+1
source share

This is not possible (also see Monkeypatching For Humans ) - possibly with DLR and method_missing .

0
source share

Well, I find this a bit complicated. If you pass Button as a method parameter:

 Button button = BlahBlah(o); window.Display(button); 

then there is a suitable class method that always takes precedence over the extension method.

But if you pass in an object that is not Button , then the appropriate class method and extension method will not be applied.

 var o = new object(); window.Display(o); 

So, from what I see, your example should work correctly, and the extension method will call the Display method on the Window instance. An infinite loop may be called by some other code.

Is there a chance that the Window class that contains the Display method in your example and the Window class that is the parameter of the extension method are actually two different classes?

0
source share

All Articles