Is there a C# feature similar to PHP __call?

Asked

Viewed 281 times

6

I have a lot of experience with PHP and, as I’m starting now with C#, it will be common for me to want to compare one language with another to find out if I can do something similar.

In PHP, I can add a feature to a class so that when the method does not exist, an action is executed. It’s the magic method __call. That is, it adds a dynamic call to methods that have not been defined in the class based on what is defined in __call.

According to the Manual:

__call() is triggered by invoking inaccessible methods in an object context.

For example,

 class Dynamic {

        public function action() {
           return 'existe';
        }

        public function __call($method, $parameters) {
              return "Não existe, mas farei alguma coisa com {$method}";
        }
 }


 $d = new Dynamic();

 $d->action(); // 'existe'

 $d->naoExisteEsseMetodo(); // 'Não existe, mas farei alguma coisa com naoExisteEsseMethodo'

Note that the last method called does not exist, but "something is done" anyway.

Is there any way to create functionality similar to __call of PHP in C#?

4 answers

8

Do not try to compare C# to PHP!

I do not say this out of malice, but because they are different languages with their particularities. C# is extremely typed, that is, it is not possible to do something of this level naturally. However, in version 4.0 of C#, Dynamic type, where you can do something "similar".

static void Main(string[] args)
{
    ExampleClass ec = new ExampleClass();
    
    dynamic dynamic_ec = new ExampleClass();
   
    dynamic_ec.exampleMethod1(10, 4);
     // As seguintes chamadas não causam erros do compilador, se 
    // existe ou não métodos apropriados. 
    dynamic_ec.someMethod("some argument", 7, null);
    dynamic_ec.nonexistentMethod();
}

class ExampleClass
{
    public ExampleClass() { }
    public ExampleClass(int v) { }

    public void exampleMethod1(int i) { }

    public void exampleMethod2(string str) { }
}

Another way is also to use Reflections, as spoken in reply from @jbueno., something like this:

ExampleClass ec = new ExampleClass();
Type type = ec .GetType();
MethodInfo m = type.GetMethod("nonexistentMethod");
m.Invoke(ec , new object[] {});

However, it is worth noting that me I see no need to use something like that, not in the context of the question.

References:

  • 1

    I put this code to test and I couldn’t even fix some things. Do you know if you have to move anything else? https://dotnetfiddle.net/gFGSCl

  • @bigown In your example, ideone can not access the class, just put as public that will have the result shown on the MS site, ie an error message saying that the method does not exist. According to the tutorial, this way is not to give errors in the compilation, ie build. I translated the part of the comments that said that and added to the reply. Thank you for the warning.

  • True, that changes the error. What you meant is that the error changes from build time to run time so?

  • @bigown Yes. There is no treatment to perform anything if you do not find the method, and as the jbueno response already had this, I found it unnecessary to add.

8


You’re looking for class DynamicObject and others of the namespace System.Dynamic, beyond the keyword itself dynamic which tells the compiler not to check for possible errors in access to members of this object leaving it for runtime to resolve this or issue error.

One of the possible actions of this class is to execute some method in the absence of the called method, this is done with the TryInvokeMember().

using System;
using System.Dynamic;
 
class Dinamica : DynamicObject {
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        result = null;
        Console.WriteLine($"Executando método \"{binder.Name}\".");
        return true;
    }
}
 
public class Program {
    public static void Main(string[] args) {
        dynamic din = new Dinamica();
        din.NaoExiste();
    }
}

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

If you want to make a more sophisticated class:

using System;
using System.Dynamic;
using System.Reflection;
using System.Collections.Generic;
using static System.Console;

public class Dynamic : DynamicObject {
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result) => dictionary.TryGetValue(binder.Name, out result);

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        dictionary[binder.Name] = value;
        return true;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
        if (dictionary.ContainsKey(binder.Name)) {
            ((Action)dictionary[binder.Name]).Invoke(); //fiz gambi, precisa elaborar mais esta chamada para adequadar para qualquer tipo
            result = "Método dinâmico executou";
            return true;
        }
        try {
            result = (typeof(Dictionary<string, object>)).InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, dictionary, args);
        } catch {
            result = "Resultado falho";
            WriteLine($"Executando método \"{binder.Name}\".");
        }
        return true;
    }

    public void Print() {
        foreach (var pair in dictionary) {
            if (!(pair.Value is Delegate)) WriteLine(pair.Key + " " + pair.Value);
        }
        if (dictionary.Count == 0) WriteLine("Sem membros para listar");
    }
}

The use:

public class Program {
    public static void Main(string[] args) {
        dynamic din = new Dynamic(); //precisa ser dynamic para o compilador não reclamar dos membros dinâmicos
        din.Nome = "Walla"; //criando membros dinamicamente
        din.Sobrenome = "C#";
        din.Action = new Action(() => WriteLine("Action Existe")); //isto não era necessário
        din.Print(); //chama um método existente na classe
        din.Action(); //chama o método que acabou de ser criado
        din.Clear(); //chama um método disponível no dicionário interno, mas que não está definido na classe
        din.Print(); //tá limpo
        din.NaoExiste(); //este método não existe
        dynamic exp = new ExpandoObject();
        exp.Action = new Action(() => WriteLine("Expando")); //só para mostrar que é possível fazer de forma automática, mas precisaria pesquisar
        exp.Action();
    }
}

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

Some considerations

A class like this is so flexible that you can make one and use for anything.

Under production conditions many improvements would be required in this code.

An example of gambiarra: I made one cast for the guy Action that under normal conditions I could not know what is the type at compile time. To make the call the right way for any method signature would have to produce an infrastructure similar to that of the class ExpandoObject did. So much so that I even use it to show that creating dynamic methods is possible. A ExpandoObject is a dynamic class ready to use, but it is not as flexible.

The example allows you to create and access existing properties and methods in the class, in another class, created in the default instance and execution (fallback) which is the focus of the question.

Dynamism

It should be noted that this class places a cover on top of a dictionary to store members. Some prefer to be explicit and use this raw data structure to store the data. It makes sense if you don’t like syntactic sugar.

We conclude that C# has several advantages over PHP and the disadvantages are minimized. It is not recommended, but if you want you can make a whole system and even more flexible than PHP.

This is a feature that should be avoided, but where it is useful can be used equal or better than in PHP, this is true for handling various external files (CSV, JSON, XML, etc.), integration with other dynamic languages or products that use dynamism (a lot from Microsoft is like that) and can make a system extremely configurable and flexible if you know what you’re doing. In some types of product this is fundamental. Alias if programmers knew how to use resources of this type could eliminate a lot of repetitive work that they don’t even realize.

Remembering that the use of dynamism requires much more attention on the part of the programmer, and extra checks on the code are necessary. Performance leaves something to be desired in these cases (do not use where it is important).

  • 1

    Until today I never had to use in production the Dynamicobject, I only had to use the Dynamic only for a couple of cases, I remember that one of them was for data import of a TXT. A case that I find quite useful this dynamism of C# as an example are the Signalr Hubs, which serve to map JS function calls. I find it a stylish solution instead of needing to define interfaces for this, as is done in other frameworks.

  • 1

    About the Signalr Hubs: http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server

  • 1

    This is a good example of how to use this dynamism that C# allows us to elaborate in our code.

  • Was this just an addendum? I was negative on the answer now, I was wondering if you found something wrong with it. Not that it was yours. It’s just that I wanted to know what’s wrong here. It’s true for those who have.

  • @Wallacemaxters can be by gambiarra. Then we wait for the person to put the correct solution. I only did it because it was useful information and does not affect the focus of the question.

  • It was an addendum, I did not deny it. I agree with what was said.

  • 1

    Those who denied it wanted to put you down, because there is no way in this solution. A screwed gamb would be to send IL code manually in order to generate a class at runtime and then call via reflection... this yes is gambiarra... = D

Show 2 more comments

6

As far as I know, it doesn’t exist. For the simple fact that C# is completely different from PHP.

If you try to call a method that does not exist in C#, the application will not compile and ready. No strategy is required in case you try to call a method that does not exist. Of course you can "try to" call a method that does not exist using Reflection, then it would be necessary to implement some strategy to implement something when the method does not exist.

Example, if you try something like obj.GetType().GetMethod("MetodoInexistente").Invoke(a, new object[0]) will receive a NullReferenceException.

Vide this answer in Stackoverflow, to a question similar to your. In that case, it’s used obj.CallOrDefault("MetodoInexistente", arg1, arg2);

public static class PhpStyleDynamicMethod
{
    public static void __call(Type @class, object[] parameters)
    {
        // Aqui vai a implementação do "__call"
    }

    public static object CallOrDefault(this object b, string method, params object[] parameters)
    {
        var methods = b.GetType().GetMethods().Where(m => m.Name == method && DoParametersMatch(m.GetParameters(), parameters));

        var count = methods.Count();

        if (count == 1)
            return methods.Single().Invoke(b, parameters);

        if (count > 1)
            throw new ApplicationException("could not resolve a single method.");

        __call(b.GetType(), parameters);

        return null;
    }
}
  • In fact, the functionality is not only to "have a strategy for a method that does not exist". In dynamic languages like php and python it is common, to create functionalities, for example, the Fluent Pattern, where I could call the methods like this: a.b().c().d().e().f() among other things you could do using creativity.

  • I still don’t understand the need. In statically typed languages like C#, you can do a.b().c().d().e().f(). Since, the type returned in the method b() has the method c() and so on.

  • It’s just an example. I can for example use the __call and check whether the $method begins with set or get and create a dynamic rule for defining class attributes (such as setNome, setIdade, without these existing)

  • These "magic" are things of dynamic language, right?

  • I’m not the best person to affirm anything, but I think so.

  • @Wallacemaxters C# is so good that if you want to make it dynamic, you can. It is not recommended, but in cases where this is useful, you can use it punctually as if it were PHP. Of course there are solutions that will not be so convenient. I will improve my response to this.

Show 1 more comment

0

Well ... one of the basic differences between C# and PHP is that C# is a TYPED language, meaning that types are known with their characteristics (methods and properties), whereas PHP is an untyped language, so it has a generic method that when called something that does not exist this is invoked.

The good thing about being typed is that it helps the development tool a lot to help you, since Intellisense (command hint when you start typing) can already predict what has that object, including with extended methods which you yourself can create and extend a method to a system class!

So... I don’t see why C# have something like "_CALL" once you use the project you will know all the available features, including if this is a DLL referenced to your project.

Welcome to the world . NET!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.