Warum sind einige Objekteigenschaften UnaryExpression und andere MemberExpression?

75

Auf die Antwort auf meine Frage reagieren Wählen Sie eine Modelleigenschaft mit einem Lambda und nicht mit einer Frage zum Namen der Zeichenfolgeeigenschaft aus , um einer Sammlung Eigenschaften wie folgt hinzuzufügen:

var props = new ExportPropertyInfoCollection<JobCard>();
props.Include(model => model.BusinessInstallNumber).Title("Install No").Width(64).KeepZeroPadding(true);
props.Include(model => model.DeviceName).Title("Device").Width(70);
props.Include(model => model.DateRequested).Title("Request Date").Format("{0:dd/MM/yyyy}").Width(83);

Ich habe den folgenden Code in die IncludeMethode geschrieben:

public class PropertyCollection<T>
{
    public void Include(Expression<Func<T, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression != null)
        {
            var pes = new ExportPropertyInfoBuilder {Property = new ExportPropertyInfo {Property = memberExpression.Member as PropertyInfo}};
            Properties.Add(pes.Property.Property.Name, pes.Property);
            return pes;
    }

Um jedoch den Code auf ausgeführt wird , fand ich das lambda einig ergab Member Werte wie erwartet, aber andere ergaben UnaryExpression Werte. Ich musste die erste Codezeile wie folgt ändern, bevor ich alle meine Eigenschaften mit Lambdas hinzufügen konnte:

var memberExpression = expression.Body as MemberExpression ?? ((UnaryExpression) expression.Body).Operand as MemberExpression;

Alle Eigenschaften sind 'einfache' Typen, dh Zeichenfolge, DateTime, int, bool usw. in einem POCO-Geschäftsobjekt. Sie sind mit verschiedenen DataAnnotations- Attributen versehen.

Was bewirkt, dass einige der Lambdas in meinem Beispiel MemberExpression- Werte und andere UnaryExpression- Werte liefern? In meinem Beispiel befindet sich die erste UnaryExpression in der dritten Zeile, der DateTime- Eigenschaft, aber boolesche Eigenschaften führen auch zu UnaryExpressions .

ProfK
quelle
1
Tritt der UnaryExpression-Ausdruck möglicherweise auf, wenn nullfähige Spalten vorhanden sind (oder nicht vorhanden sind) ?
Leppie
@ leppie, ich vermute, es ist in nicht nullbaren Spalten. In meinem Beispiel befindet sich der erste UnaryExpression in einer DateTime, wobei sich die vorherigen MemberExpressions in Zeichenfolgen befinden. Eine nachfolgende UnaryExpression befindet sich auf einem Bool.
ProfK
Ich werde ein bisschen nachforschen. Ich habe MemberExpressionsolche Ausdrücke schon einmal verwendet (oder eher missbraucht) und hatte nie Probleme, was bedeutet, dass mein Code immer fehlschlagen würde. Auf welcher .NET-Version laufen Sie? Ich benutze .NET 4 noch nicht.
Leppie
Sie sind sich nicht sicher, ob es wichtig ist, aber können Sie den erwarteten Ausdruckstyp Includeangeben , der erwartet wird? (einschließlich Einschränkungen)
Leppie

Antworten:

61

Ich glaube ich weiß was das Problem ist. Ihr Ausdruck gibt den Typ zurück object.

Wenn Sie dies in Expression<Func<T, R>>den Rückgabetyp ändern, sollte dieser korrekt abgeleitet werden und UnaryExpression(von dem ich annehme, dass es sich um eine Boxoperation handelt) nicht auftreten.

Aktualisieren:

Die Unterschrift für Includesollte sein:

public void Include<T, R>(Expression<Func<T, R>> expression)
Leppie
quelle
1
Entschuldigen Sie meine Dummheit hier, aber was soll R sein? Ich kann es nicht zu MemberExpression machen, da sich MemberExpression in der Body-Eigenschaft des Ausdrucks befindet. Ich bin damit einverstanden, dass der UnaryExpression wahrscheinlich auf Boxen zurückzuführen ist.
ProfK
4
@ProfK: R wird einfach abgeleitet, es ist der Typ der Eigenschaft, die zurückgegeben wird. Sie werden es wahrscheinlich nicht benutzen, aber Sie könnten :)
Leppie
2
Ich glaube ich sehe was passiert. Da der Ausdrucksrückgabetyp Objekt ist, wird er eingerahmt. Bei einem typisierten Rückgabetyp ist dies nicht der Fall. Danke @leppie! Vertraue einem funktionierenden Mann, der hier hilft :-)
ProfK
1
Gute Antwort. Sollte die Signatur für Include nicht lauten: public void Include <R> (Ausdruck <Func <T, R >>), da T bereits von der PropertyCollection-Klasse definiert ist.
Xcalibur
3
@leppie Der generische Typparameter T ist bereits auf Klassenebene definiert, daher sollte entweder ein anderer Typparametername verwendet werden, wenn die Absicht unterschiedlich ist, oder er ist redundant
Xcalibur