4

Is it possible to exchange implementations of methods in C#, like method swizzling in Objective-C?

So I could replace an existing implementation (from an outside source, via a dll for example) at runtime with my own (or add another onto it).

I have searched for this, but have found nothing of value.

vrwim
  • 13,020
  • 13
  • 63
  • 118

4 Answers4

4

You could use delegates to have your code point to whatever method you wish to execute at run time.

public delegate void SampleDelegate(string input);

The above is a function pointer to any method which yields void and takes a string as input. You can assign any method to it which has that signature. This can also be done at run time.

A simple tutorial can be also found on MSDN.

EDIT, as per your comment:

public delegate void SampleDelegate(string input);
...
//Method 1
public void InputStringToDB(string input) 
{
    //Input the string to DB
}
...

//Method 2
public void UploadStringToWeb(string input)
{
    //Upload the string to the web.
}

...
//Delegate caller
public void DoSomething(string param1, string param2, SampleDelegate uploadFunction)
{
    ...
    uploadFunction("some string");
}
...

//Method selection:  (assumes that this is in the same class as Method1 and Method2.
if(inputToDb)
    DoSomething("param1", "param2", this.InputStringToDB);
else
    DoSomething("param1", "param2", this.UploadStringToWeb);

You can also use Lambda Expressions: DoSomething("param1", "param2", (str) => {// what ever you need to do here });

Another alternative would be to use the Strategy Design Pattern. In this case, you declare interfaces and use them to denote the behavior provided.

public interface IPrintable
{
    public void Print();
}

public class PrintToConsole : IPrintable
{
    public void Print()
    {
        //Print to console
    }
}

public class PrintToPrinter : IPrintable
{
    public void Print()
    {
        //Print to printer
    }
}


public void DoSomething(IPrintable printer)
{
     ...
     printer.Print();
}

...

if(printToConsole)
    DoSomething(new PrintToConsole());
else
    DoSomething(new PrintToPrinter());

The second approach is slightly more rigid than the first, but I think it is also another way to go around achieving what you want.

npinti
  • 51,780
  • 5
  • 72
  • 96
  • And how would I go about replacing method implementations with it? – vrwim Dec 29 '15 at 08:13
  • @vrwim You simply can't, it's a different approach. But what you can do is to call the delegate inside a method. – Felix K. Dec 29 '15 at 08:17
  • @FelixK: If I am understanding the question, I think that you can actually do that by passing along lambda expressions instead of actual function pointers. Lambda expressions eventually boil down to delegates. – npinti Dec 29 '15 at 08:28
  • @npinti this is not shat the question is about, method swizzling in Objective-C replaces the method implementation the runtime will invoke, so for example you could have a `Foo` class with `bar` implementation and then swizzle the method so every time `bar` is called, the runtime actually executes your method. This is a runtime feature and doesn't require the creator of the `Foo` class to do anything. – JustSid Dec 29 '15 at 08:36
  • It's super hacky and should be avoides if possible, but it's a nice last ditch effort to have to work around bugs in third party frameworks. – JustSid Dec 29 '15 at 08:37
  • @JustSid: If I am understanding you correctly, isn't that achieved by passing on a lambda expression? Or do you mean change the way which code you do not have access to behaves? – npinti Dec 29 '15 at 08:47
  • @npinti No, it's not passing anything extra. It completely changes which code is actually being executed, no matter who the caller is. And none of the callers has to be aware of it or prepare their code, it essentially replaces which method the dynamic dispatch will call. – JustSid Dec 29 '15 at 09:05
  • @JustSid: In that case I do not think that that is possible no/ – npinti Dec 29 '15 at 09:12
  • @npinti I figured. I just wanted to clarify what the original question was after. I don't think passing a lambda will do for the OP since that also works in Objective-C, and method swizzling is discouraged unless everything else is exhausted (and even then one should ask themselves if this is really the right path to go down). – JustSid Dec 29 '15 at 09:14
  • @JustSid indeed that is what I am after, and it seems the C# runtime just doesn't allow this. – vrwim Dec 29 '15 at 09:18
0

The only way to "replace methods" is to use delegates.

If your code looks like this:

public void Foo()
{
    Bar();
}

public void Bar()
{
}

Then you cannot get Foo to call any other method than Bar. The method dispatch table you refer to in Objective-C is not mutable in .NET.

To be able to specify which method Foo should call above you need to use a delegate:

public void Foo(Action whichMethod)
{
    whichMethod();
}

And you could call it like this:

Foo(Bar);
Foo(Baz);

But the method has to be built to allow this kind of runtime replacement.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
0

While this isn't the best path to object-oriented programming in a strongly-typed language, it's worth to mention that since .NET 4.0, C# has included the Dynamic Language Runtime (DLR) which allows dynamic programming. One of most curious dynamic objects is ExpandoObject: a fully runtime-expandable object:

dynamic expando = new ExpandoObject();
expando.DoStuff = new Func<string>(() => "hello world");

// Now you can swap DoStuff with other method setting another delegate:
expando.DoStuff = new Func<string, string>(text => text + "!");

BTW, as I said above, I've shared this approach here just for learning purposes. It might be useful in some edge cases, but as C# is a compiled and strongly-typed language, you should avoid this approach in 99.99% of cases.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • Thanks for this information, but this is not what I was looking for, I guess it just isn't possible. – vrwim Jan 01 '16 at 22:04
  • @vrwim Well, you're looking to exchange methods.... BTW I added this answer to open curiosity to future readers... – Matías Fidemraizer Jan 01 '16 at 22:21
  • @vrwim You should rephrase your question and don't ask for the solution but explain what you're trying to achieve or what's your goal, because maybe you're trying to implement something the objective-c way in c#, and in c# there's a better approach for the same problem – Matías Fidemraizer Jan 01 '16 at 22:21
-1
void Test(Action method) {
     if ( method != null ) method.invoke();
}

you can call this

Test( () => { Console.WriteLine("hello world"); } )

change def and call again

Test( () => { MessageBox.Show("Hi"); } )
Nikson Kanti Paul
  • 3,394
  • 1
  • 35
  • 51