2013-05-10 16 views
8

Ho un'interfaccia (che è utilizzato dal repository) che ha questo membro:lavoro intorno mancanza di parziale tipo generico inferenza con vincoli

T FindById<T, TId>(TId id) 
    where T : class, IEntity<TId> 
    where TId : IEquatable<TId>; 

Questo permette al chiamante di specificare un tipo di entità (T) e la tipo di esso è il campo Id (TId). L'implementatore di questa interfaccia troverà quindi le entità di tipo T e utilizzerà il parametro id per filtrarli in base al loro ID (definito su IEntity<TId>).

Attualmente sto chiamando in questo modo:

int id = 123; 
var myApproval = PartsDC.FindById<Approval, int>(id); 

Idealmente mi piacerebbe fare questo:

int id = 123; 
var myApproval = PartsDC.FindById<Approval>(id); 

Ho letto le risposte a questa domanda:

Partial generic type inference possible in C#?

Capisco che non riesco a ottenere la sintassi che voglio, ma posso avvicinarmi. Non riesco a farlo funzionare correttamente nel mio caso, ma a causa dei miei limiti di parametri generici.

Ecco quello che ho finora:

public class FindIdWrapper<T> where T : class 
{ 
    public readonly IDataContext InvokeOn; 

    public FindIdWrapper(IDataContext invokeOn) 
    { 
     InvokeOn = invokeOn; 
    } 

    T ById<TId>(TId id) where TId : IEquatable<TId> 
    { 
     return InvokeOn.FindById<T, TId>(id); 
    } 
} 

public static class DataContextExtensions 
{ 
    public static FindIdWrapper<T> Find<T>(this IDataContext dataContext) where T : class, IEntity 
    { 
     return new FindIdWrapper<T>(dataContext); 
    } 
} 

l'errore di compilazione che ottenga è:

The type 'T' cannot be used as type parameter 'T' in the generic type or method 'PartsLegislation.Repository.IDataContext.FindById<T,TId>(TId)'. There is no implicit reference conversion from 'T' to 'PartsLegislation.Repository.IEntity<TId>'.

ho capito quello che sta dicendo, perché il T nella mia classe wrapper è vincolato solo per essere un tipo di riferimento, ma la funzione FindById vuole che sia IEntity<TId>, ma non posso farlo perché il metodo TId è nel metodo (altrimenti sono tornato in quadrato uno).

Come posso aggirare questo problema (o non posso)?

+0

Non si può aggiungere 'TID' come vincolo a' FindIdWrapper'? – Nick

+0

@Nick no, perché il 'TID' non è noto al punto dell'applicazione parziale –

risposta

4

Questo non può funzionare nel solito modo perché non è possibile convincere il compilatore del vincolo TId dopo il fatto. È tuttavia possibile invertire la sequenza, ad esempio

var obj = ById(id).Find<SomeType>(); 

Non elegante, ma funziona. Implementazione:

public Finder<TId> ById<TId>(TId id) where TId : IEquatable<TId> 
{ 
    return new Finder<TId>(this, id); 
} 
public struct Finder<TId> where TId : IEquatable<TId> 
{ 
    private readonly YourParent parent; 
    private readonly TId id; 
    internal Finder(YourParent parent, TId id) 
    { 
     this.id = id; 
     this.parent = parent; 
    } 
    public T Find<T>() where T : class, IEntity<TId> 
    { 
     return parent.FindById<T, TId>(id); 
    } 
} 

avvertenza: è probabilmente più semplice indicare semplicemente entrambi i tipi di parametro.

+0

Stavo solo scrivendo lo stesso nella mia risposta .. Marc sei troppo veloce! Io uso lo stesso approccio nel mio codice, e vorrei anche poter scrivere qualcosa di più elegante .. –

+0

Grande, grazie! Per essere onesti, una volta capito che non potevo fare un'inferenza parziale di tipo generico, ero per lo più accademico. :) –

+0

Ho nomi leggermente diversi per renderlo "più bello": 'Ricerca (id). Da ()'. –

Problemi correlati