Wie bekomme ich Objektinstanz von () => foo.Title Ausdruck

.net-3.5 c# data-binding expression-trees lambda

Frage

Ich habe eine einfache Klasse mit einer Eigenschaft

class Foo 
{ 
    string Title { get; set; } 
}

Ich versuche, die Datenbindung durch Aufruf einer Funktion wie zu vereinfachen

BindToText(titleTextBox, ()=>foo.Title );

was wie erklärt wird

void BindToText<T>(Control control, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;

    control.DataBindings.Add("Text", ??? , name);
}

also was lege ich hinein ??? für die Instanz meiner Foo Klasse. Wie bekomme ich vom lambda-Ausdruck eine Referenz auf die aufrufende foo Instanz?

edit: Die Instanz sollte irgendwo da sein, da ich property.Compile() aufrufen und einen Delegaten erstellen kann, der die foo Instanz in meiner BindToText Funktion verwendet. Also meine Frage ist, ob dies getan werden kann, ohne einen Verweis auf die Instanz in den Funktionsparametern hinzuzufügen. Ich rufe Occum's Razor auf, um die einfachste Lösung zu liefern.

edit 2: Was viele nicht bemerkt haben, ist die Schließung , die beim Zugriff auf die Instanz von foo in meiner Funktion existiert, wenn ich das Lambda kompiliere. Woher kommt der Compiler, wo die Instanz zu finden ist, und ich nicht? Ich bestehe darauf, dass es eine Antwort geben muss, ohne ein zusätzliches Argument zu übergeben.


Lösung

Dank VirtualBlackFox ist die Lösung so:

void BindText<T>(TextBoxBase text, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;
    var fex = mex.Expression as MemberExpression;
    var cex = fex.Expression as ConstantExpression;            
    var fld = fex.Member as FieldInfo;
    var x = fld.GetValue(cex.Value);
    text.DataBindings.Add("Text", x, name);            
}

was mir erlaubt, einfach BindText(titleText, () => foo.Title); .

Akzeptierte Antwort

Kleines LINQPad- Beispiel von dem, was Sie wollen:

void Foo<T>(Expression<Func<T>> prop)
{
    var propertyGetExpression = prop.Body as MemberExpression;

    // Display the property you are accessing, here "Height"
    propertyGetExpression.Member.Name.Dump();

    // "s" is replaced by a field access on a compiler-generated class from the closure
    var fieldOnClosureExpression = propertyGetExpression.Expression as MemberExpression;

    // Find the compiler-generated class
    var closureClassExpression = fieldOnClosureExpression.Expression as ConstantExpression;
    var closureClassInstance = closureClassExpression.Value;

    // Find the field value, in this case it's a reference to the "s" variable
    var closureFieldInfo = fieldOnClosureExpression.Member as FieldInfo;
    var closureFieldValue = closureFieldInfo.GetValue(closureClassInstance);

    closureFieldValue.Dump();

    // We know that the Expression is a property access so we get the PropertyInfo instance
    // And even access the value (yes compiling the expression would have been simpler :D)
    var propertyInfo = propertyGetExpression.Member as PropertyInfo;
    var propertyValue = propertyInfo.GetValue(closureFieldValue, null);
    propertyValue.Dump();
}

void Main()
{
    string s = "Hello world";
    Foo(() => s.Length);
}

Beliebte Antwort

Nicht. Ändern Sie einfach die Methode, um einen anderen Parameter zu verwenden, wie in # 3444294 beschrieben . Für Ihr Beispiel könnte das etwa so aussehen:

void BindToText<T>(Control control, T dataSource, Expression<Func<T>> property)
{
    var mex = property.Body as MemberExpression;
    string name = mex.Member.Name;

    control.DataBindings.Add("Text", dataSource, name);
}

und würde wie gerufen werden

BindToText(titleTextBox, foo, ()=>foo.Title );

Immer noch nett, aber leicht zu verstehen. Es passiert keine Magie. ;)



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum