2013-04-04 17 views
5

Hier spezifiziert ist ein Ausschnitt aus einer Klasse I für die Prüfung Typ Erweiterungsmethoden verwenden:eine generische Erweiterungsmethode aufrufen, ohne so viele Arten

class Something 
{ 
    [StringLength(100, MinimumLength = 1, ErrorMessage = "Must have between 1 and 100 characters")] 
    public string SomePublicString { get; set; } 
} 

Ich habe folgende Erweiterungsmethode:

public static class TypeExtensions 
{ 
    public static TAttributeType GetCustomAttribute<T, TAttributeType, TProperty>(this T value, Expression<Func<T, TProperty>> propertyLambda, bool inherit = false) 
    { 
    var type = typeof(T); 
    var member = (MemberExpression)propertyLambda.Body; 
    var propertyInfo = (PropertyInfo)member.Member; 
    var customAttributes = propertyInfo.GetCustomAttributes(typeof(TAttributeType), inherit); 

    return customAttributes.OfType<TAttributeType>().FirstOrDefault(); 
    } 
} 

Verwendung in einem Komponententest:

1: var something = new Something(); 
2: var actual = something.GetCustomAttribute<Something, StringLengthAttribute, string>(x => x.SomePublicString); 

3: actual.MinimumLength.Should().Be(1); 
4: actual.MaximumLength.Should().Be(100); 
5: actual.ErrorMessage.Should().Be("Must have between 1 and 100 characters"); 

Dies gibt zurück ein bestandener Test (unter Verwendung von FluentAssertions).

aber ich möchte den Methodenaufruf zu GetCustomAttribute() in Zeile 2 bis auf die folgenden erhalten:

var actual = something.GetCustomAttribute<StringLengthAttribute>(x => x.SomePublicString); 

Ist das möglich? Fehle ich etwas? Vielleicht bin ich auf einen Koffein-Crash. :(

Antwort

7

Nein, du hast entweder alle die Typargumente angeben, oder keiner von ihnen. Es gibt keine "partielle" Inferenz.

Eine Möglichkeit, mit der Sie manchmal davonkommen können, ist jedoch eine generische Methode mit einem einzigen Argument, die eine Instanz einer generischen Klasse zurückgibt und dann die Typinferenz für den Rest verwendet - oder umgekehrt . In diesem Fall könnte es tatsächlich am besten sein, das Attribut vom Rest holen zu trennen sowieso:

something.GetPropertyAttributes(x => x.SomePublicString) 
     .FirstAttribute<StringLengthAttribute>(); 

Hier ist der erste Methodenaufruf folgert T und TProperty - und gerade gibt eine IEnumerable<Attribute> und FirstAttribute würden nur tun, um die OfType/FirstOrDefault Anrufe. (Sie könnten sogar entscheiden, dass Sie FirstAttribute nicht benötigen, da es ziemlich einfach ist, OfType und selbst aufzurufen.)

+1

Huzzah für das Prinzip der einheitlichen Verantwortung. – StriplingWarrior

+0

Danke. Ich hatte ursprünglich etwas wie Ihr Code-Beispiel, die "PropertyInfo" -Reflexion, benutzerdefinierte Attribut-Reflexionen und schließlich den Typ, den ich wollte, aber ich hoffte auf etwas ... schlanker. :-) –

+0

@Dan, ich habe oft gehört, dass diese Beschwerde auf diese Lösung angewendet wurde und habe nie wirklich verstanden, woher sie kommt.Scheint ziemlich mager zu mir (für die Verbraucher). –

3

Es ist nicht möglich, einen Teil zu schließen, aber nicht alle, der Typ Argumente, keine

Sie können entweder:.

  1. alle allgemeinen Argumente angeben

  2. .
  3. Stellen Sie sicher, dass alle generischen Argumente abgeleitet werden können. (Nicht immer möglich oder die Kompromisse, die erforderlich sind, um Rückschlüsse zuzulassen, sind möglicherweise nicht wünschenswert.)

  4. Brechen Sie die Methode in mehrere Methoden auf, sodass eine Methode die generischen Argumente enthält und eine nicht.

+0

Danke. Meine Erweiterungsmethode enthält jedoch keine Typen, da ich alle verwendeten Typen angeben muss. Ist das so "sauber" wie ich den Methodenaufruf bekommen kann? –

+1

@DanAtkinson Ohne die Signatur der Methode zu ändern, ja. Wie ich schon sagte, Schlussfolgerung ist ein Alles oder nichts. – Servy

Verwandte Themen