c# - que - microsoft csharp runtimebinder runtimebinderexception no se puede invocar un tipo no delegado
Crear un delegado desde un método de obtención o establecimiento de propiedades. (3)
El truco es que una Property
es en realidad solo una fachada de los métodos de captador y / o establecedor reales que están ocultos. El compilador emite estos métodos y los nombra de acuerdo con el nombre de la Property
junto con get_ y set_ , respectivamente. En el siguiente ejemplo, sería int get_Value()
y void set_Value(int)
. Así que solo evite la llamada "propiedad" y siga directamente hacia esos métodos.
Con el método getter y / o setter tenemos dos opciones.
Podemos crear un delegado vinculado que tiene
this
valor para algunas instancias "grabadas". Esto es similar a lo que espera de la propiedad en sí, es decir, este delegado solo será bueno para acceder a esa instancia de tiempo de ejecución. La ventaja es que, dado que el delegado está permanentemente vinculado a su instancia, no tiene que pasar un argumento adicional.La otra opción es crear delegados que no estén asociados con una instancia de destino específica. Aunque estos llaman exactamente a los mismos métodos de acceso a la propiedad que antes, en este caso la propiedad
Target
del delegado está vacía / nula. Al no tener que usarthis
puntero, la firma del método para un delegado no vinculado se modifica para revelar el famoso puntero " oculto esto ".
Más discusión a continuación, pero primero aquí está el código. Ilustra los cuatro casos, getter / setter -vs- bound / unbound.
partial class Cls
{
static Cls()
{
UnboundGet = create<Func<Cls, int>>(null, mi_get);
UnboundSet = create<Action<Cls, int>>(null, mi_set);
}
public Cls()
{
BoundGet = create<Func<int>>(this, mi_get);
BoundSet = create<Action<int>>(this, mi_set);
}
public readonly static Func<Cls, int> UnboundGet;
public readonly static Action<Cls, int> UnboundSet;
public readonly Func<int> BoundGet;
public readonly Action<int> BoundSet;
public int Value { get; set; }
};
NB, esto se refiere a algunos códigos de ayuda que se incluyen al final de esta publicación.
Para resumir, la "firma verdadera" del método de instancia es idéntica al caso del delegado vinculado, pero se cancela. Los delegados limitados se encargan de proporcionarlo, como primer argumento, al proporcionar la instancia que llevan en esa propiedad de Target
. Los delegados no vinculados son universales, por lo que nunca necesita más que un solo par de getter / setter por propiedad. Se pueden utilizar para acceder a esa propiedad de instancia en cualquier instancia de tiempo de ejecución pasada, presente o futura, pero esto significa que tiene que pasar explícitamente un objetivo deseado en this
objeto como primer argumento cada vez que invoque al captador / definidor.
Tenga en cuenta también que aunque los delegados no vinculados aquí tienen acceso a métodos o propiedades de instancia , en realidad no necesita ninguna instancia de Cls
tiempo de ejecución viable para crear el delegado.
Aquí hay una demo.
static class demo
{
static demo()
{
var c1 = new Cls { Value = 111 };
var c2 = new Cls { Value = 222 };
Console.WriteLine("c1: {0} c2: {1}", c1, c2);
c1.BoundSet(c1.Value + 444);
Cls.UnboundSet(c2, c2.BoundGet() + 444);
Console.WriteLine("c1: {0} c2: {1}", c1, c2);
}
};
Y la salida:
c1: 111 111 111 c2: 222 222 222 c1: 555 555 555 c2: 666 666 666
Finalmente, aquí hay algunas cosas de ayuda que pongo aquí para reducir el desorden. Tenga en cuenta que los MethodInfo
s pueden almacenarse en caché y reutilizarse si planea construir muchos delegados vinculados. Si, en cambio, prefiere utilizar los delegados no vinculados (estáticos), no necesitará mantenerlos cerca; porque los delegados no vinculados funcionan universalmente para cualquier instancia, por lo que puede decidir que nunca necesita crear ningún delegado vinculado.
partial class Cls
{
static MethodInfo mi_get = typeof(Cls).GetMethod("get_Value"),
mi_set = typeof(Cls).GetMethod("set_Value");
static T create<T>(Object _this, MethodInfo mi) =>
(T)(Object)Delegate.CreateDelegate(typeof(T), _this, mi);
public override String ToString() =>
String.Format("{0} {1} {2}", Value, BoundGet(), Cls.UnboundGet(this));
}
Para crear un delegado a partir de un método, puede utilizar la sintaxis de compilación segura de tipos:
private int Method() { ... }
// and create the delegate to Method...
Func<int> d = Method;
Una propiedad es una envoltura alrededor de un método de obtención y establecimiento, y quiero crear un delegado a un método de obtención de propiedad. Algo como
public int Prop { get; set; }
Func<int> d = Prop;
// or...
Func<int> d = Prop_get;
Lo que no funciona, por desgracia. Tengo que crear un método lambda separado, que parece innecesario cuando el método getter coincide con la firma del delegado de todos modos:
Func<int> d = () => Prop;
Para poder usar el método de delegado directamente, tengo que usar una reflexión desagradable, que no es compilación de tipo seguro:
// something like this, not tested...
MethodInfo m = GetType().GetProperty("Prop").GetGetMethod();
Func<int> d = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), m);
¿Hay alguna forma de crear un delegado en un método de obtención de propiedades directamente de forma segura para la compilación, similar a crear un delegado en un método normal en la parte superior, sin necesidad de utilizar un método lambda intermedio?
Otra opción (en .NET 3.0 y más reciente) sería usar DependencyProperty
lugar de una propiedad tradicional. Luego puede pasar el objeto DependencyProperty
(en lugar de pasar a un delegado) y llamar a GetValue()
o SetValue()
cuando sea necesario.
(Sí, sé que esta es una pregunta antigua, pero era una de las publicaciones principales cuando intentaba hacer algo muy similar).
Por lo que sé, ya ha escrito todas las variantes "válidas". Dado que no es posible dirigirse explícitamente a un captador o configurador en un código normal (sin reflexión, es decir), no creo que haya una manera de hacer lo que quiere.