2010-01-30 9 views
15

Gibt es einen schnelleren Weg Fun<TEntity, TId> zu Func<TEntity, object>Schnellere Möglichkeit, einen Func <T, T2> an Func <T, object> zu senden?

public static class StaticAccessors<TEntity> 
{ 
public static Func<TEntity, TId> TypedGetPropertyFn<TId>(PropertyInfo pi) 
{ 
    var mi = pi.GetGetMethod(); 
    return (Func<TEntity, TId>)Delegate.CreateDelegate(typeof(Func<TEntity, TId>), mi); 
} 

public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi) 
{ 
    var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn"); 
    var genericMi = mi.MakeGenericMethod(pi.PropertyType); 
    var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi }); 

    //slow: lambda includes a reflection call 
    return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); //can we replace this? 
} 
} 

Gibt es eine Möglichkeit zu werfen typedGetPropertyFn zu einem Func<TEntity, object> zu konvertieren, ohne oben wie das Beispiel in der zurück Lambda Reflexionscode?

EDIT: hinzugefügt modifizierte Lösung

Ok dank 280Z28 für mich führt den richtigen Weg, die ich weiter unten in der endgültigen Lösung aufgenommen haben. Ich habe den Reflection-Code für Plattformen, die Expressions nicht unterstützen, dort gelassen. Für Plattformen, die dies tun, zeigt es eine 26x bis 27x (13/.5 Ticks avg) Perf Erhöhung für bekommen int und string Eigenschaften.

Antwort

6

Wie Sie wissen, können Sie eine MethodInfo von PropertyInfo.GetGetMethod() erhalten. Von diesem können Sie das folgende verwenden, um eine Func<object, object> abzurufen, um diese Eigenschaft abzurufen. Mit einer ähnlichen Methode können Sie eine stark typisierte Func<TObject, TResult> zurückgeben. Für jedes gegebene MethodInfo sollten Sie die Ergebnisse dieses Aufrufs zwischenspeichern, wenn Sie es mehr als einmal benötigen, da diese Methode mindestens eine Größenordnung teurer ist, als den resultierenden Delegaten aufzurufen.

private static Func<object, object> BuildAccessor(MethodInfo method) 
{ 
    ParameterExpression obj = Expression.Parameter(typeof(object), "obj"); 

    Expression<Func<object, object>> expr = 
     Expression.Lambda<Func<object, object>>(
      Expression.Convert(
       Expression.Call(
        Expression.Convert(obj, method.DeclaringType), 
        method), 
       typeof(object)), 
      obj); 

    return expr.Compile(); 
} 
5

In .NET 4.0 können Sie dies tun, weil der Func-Delegat TResult mit dem out-Modifikator markiert. .NET 3.5 unterstützt generic covariance/contravariance nicht, so dass Sie nicht einfach casten können. Ich bin mir nicht sicher, ob es eine andere schlaue Art gibt, es schneller als Nachdenken zu tun.

Hier ist the .NET 4.0 doc page for Func. Beachten Sie, dass TResult mit "out" markiert ist, so dass der Rückgabewert als weniger spezifischer Typ wie z. B. Objekt interpretiert werden kann.

Für ein schnelles Beispiel, das keine externen Abhängigkeiten hat, kann der folgende Code nicht auf .NET 3.5 kompilieren, aber kompiliert und auf .NET 4.0 ordnungsgemäß ausgeführt wird.

// copy and paste into LINQpad 
void Main() 
{ 
    Func<int, string> func1 = GetString; 
    string res1 = func1(1); 
    res1.Dump(); 

    Func<int, object> func2 = func1; 
    object res2 = func2(1); 
    res2.Dump(); 
} 

public string GetString<T>(T obj) { 
    return obj.ToString(); 
} 
+0

Was passiert, wenn meine ursprüngliche Funktion einen Werttyp wie Guid zurückgibt? Dann bekomme ich einen Laufzeitfehler beim Versuch, auf Func zu werfen. –

5

Haben Sie folgende Punkte beachtet tun:

Func<Foo, Bar> typed = (f) => return new Bar(); 
Func<Foo, object> untyped = (f) => typed(f); 

Auf diese Weise können nur die Delegierten wickeln.

+0

Ich kann dies nicht tun, weil ich nicht den Kompilierzeittyp von 'Bar' habe – mythz

+1

Wenn Sie den" Bar "-Typ zur Kompilierzeit hatten, wird dies die einfachste und" olympische "Art sein, dieses XD zu machen . –

Verwandte Themen