2013-04-04 4 views
5

Ecco un frammento di una classe che uso per prove di tipo metodi di estensione:Chiamare un metodo di estensione generica senza specificare come molti tipi

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

Ho la seguente metodo di estensione:

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(); 
    } 
} 

utilizzo in una prova di unità:

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"); 

Ciò restituisce un test di passaggio (usando FluentAssertions).

Tuttavia, mi piacerebbe ottenere il metodo di chiamata a GetCustomAttribute() in linea 2 fino alla seguente:

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

è possibile? Mi sto perdendo qualcosa? Forse sono in un incidente con la caffeina. :(

risposta

7

No, hai specificare tutti gli argomenti di tipo, o nessuno di essi. Non esiste inferenza di tipo "parziale".

Tuttavia, un modo in cui è possibile ottenere a volte questo è di avere un metodo generico con un argomento di tipo singolo che restituisce un'istanza di una classe generica e quindi utilizzare l'inferenza del tipo su quello per fare il resto - o viceversa . In questo caso, si potrebbe in realtà essere migliore di separare l'attributo recupero dal resto in ogni caso:

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

Ecco la prima chiamata di metodo deduce T e TProperty - e solo restituisce un IEnumerable<Attribute> e FirstAttribute sarebbe solo fare il OfType/FirstOrDefault chiamate. (Si potrebbe anche decidere che non è necessario FirstAttribute, dato che è abbastanza semplice chiamare OfType e FirstOrDefault.)

+1

Huzzah per il principio di responsabilità unica. – StriplingWarrior

+0

Grazie. Inizialmente avevo qualcosa come il tuo esempio di codice, rompendo il riflesso 'PropertyInfo', i riflessi degli attributi personalizzati e infine, il tipo che volevo, ma speravo in qualcosa ... più snello. :-) –

+0

@Dan, ho sentito spesso che la denuncia si applicava a questa soluzione e non ho mai veramente capito da dove proviene.Sembra abbastanza magra per me (per i consumatori). –

3

Non è possibile dedurre alcuni, ma non tutti, gli argomenti di tipo, non

è possibile:.

  1. Specificare tutti gli argomenti generici

  2. .
  3. Verificare che tutti gli argomenti generici possano essere dedotti. (Non sempre possibile, oppure i compromessi necessari per consentire l'inferenza potrebbero non essere desiderabili)

  4. Rompere il metodo in più metodi in modo che un metodo deduca gli argomenti generici e uno no.

+0

Grazie. Tuttavia, il mio metodo di estensione non deduce alcun tipo, poiché devo specificare tutti i tipi utilizzati. È 'pulito' come posso ottenere la chiamata al metodo? –

+1

@DanAtkinson Senza modificare la firma del metodo, sì. Come ho detto, l'inferenza è una cosa tutto o niente. – Servy