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).
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
– Maniero
@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.
– Randrade
True, that changes the error. What you meant is that the error changes from build time to run time so?
– Maniero
@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.
– Randrade