Zugriff auf den Indexer aus der Ausdrucksbaumstruktur

c# expression-trees

Frage

Ich arbeite an einer Filterfunktion. Der Filter wird ein von einem Benutzer erstellter Ausdrucksbaum sein. Es wird ungefähr 30 Felder geben, die der Benutzer zum Filtern verwenden kann. Ich denke, der beste Weg ist, das Objektmodell mit Indexer zu erstellen und auf die erforderlichen Werte nach dem Index des Aufzählungstyps zuzugreifen.

Siehe dieses Beispiel:

enum Field
{
    Name,
    Date,
}

class ObjectModel
{
    object this[Field Key]
    {
        get 
        {
            //...
            return xx;
        }
    }
}

Ich möchte fragen, wie kann ich auf einen Indexer von einem Ausdrucksbaum zugreifen.

Akzeptierte Antwort

Der Indexer ist eine einfache Eigenschaft, normalerweise Item . Das heißt, Sie können wie jede andere Eigenschaft auf den Indexer zugreifen, indem Sie seinen Namen verwenden.

Der Name der Indexer-Eigenschaft kann vom Implementierer der Klasse mithilfe des IndexerName Attributs IndexerName werden .

Um den tatsächlichen Namen der Indexer-Eigenschaft zuverlässig zu ermitteln, müssen Sie über die Klasse nachdenken und das DefaultMember Attribut DefaultMember .
Weitere Informationen finden Sie hier .


Beliebte Antwort

Ich werde ein vollständiges Beispiel zur Verwendung eines Indexers veröffentlichen:

ParameterExpression dictExpr = Expression.Parameter(typeof(Dictionary<string, int>));
ParameterExpression keyExpr = Expression.Parameter(typeof(string));
ParameterExpression valueExpr = Expression.Parameter(typeof(int));

// Simple and direct. Should normally be enough
// PropertyInfo indexer = dictExpr.Type.GetProperty("Item");

// Alternative, note that we could even look for the type of parameters, if there are indexer overloads.
PropertyInfo indexer = (from p in dictExpr.Type.GetDefaultMembers().OfType<PropertyInfo>()
                        // This check is probably useless. You can't overload on return value in C#.
                        where p.PropertyType == typeof(int)
                        let q = p.GetIndexParameters()
                        // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type.
                        where q.Length == 1 && q[0].ParameterType == typeof(string)
                        select p).Single();

IndexExpression indexExpr = Expression.Property(dictExpr, indexer, keyExpr);

BinaryExpression assign = Expression.Assign(indexExpr, valueExpr);

var lambdaSetter = Expression.Lambda<Action<Dictionary<string, int>, string, int>>(assign, dictExpr, keyExpr, valueExpr);
var lambdaGetter = Expression.Lambda<Func<Dictionary<string, int>, string, int>>(indexExpr, dictExpr, keyExpr);
var setter = lambdaSetter.Compile();
var getter = lambdaGetter.Compile();

var dict = new Dictionary<string, int>();
setter(dict, "MyKey", 2);
var value = getter(dict, "MyKey");

Um aus dem Indexer zu IndexExpression enthält der IndexExpression direkt den Wert der indizierten Eigenschaft. Um es zu schreiben, müssen wir Expression.Assign . Alles andere ist ziemlich Vanille Expression . Wie von Daniel geschrieben, wird der Indexer normalerweise "Item" genannt. Beachten Sie, dass Expression.Property eine Überladung hat, die den Namen des Indexers (also "Item" ) direkt akzeptiert, aber ich habe mich entschieden, es manuell zu finden (damit es wiederverwendet werden kann). Ich habe sogar ein Beispiel dafür gegeben, wie LINQ verwendet wird, um die genaue Überladung des gewünschten Indexers zu finden.

Nur als eine Kuriosität, wenn Sie auf MSDN zum Beispiel nach Wörterbuch schauen, finden Sie unter Eigenschaften Artikel



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