2016-02-18 7 views
11

Si supponga che ho un'interfaccia comeCo/controvarianza con Func <in T1, fuori TResult> come parametro di

public interface IInterface<in TIn, out TOut> { 
    IInterface<TIn, TOut> DoSomething(TIn input); 
} 

TIn essere contra -variant, e TOut essendo co -variant.

ora, voglio chiamanti di essere in grado di specificare alcune funzione da eseguire sul valore dell'ingresso, così ingenuamente vorrei aggiungere il seguente metodo per l'interfaccia:

IInterface<TIn, TOut> DoSomethingWithFunc(Func<TIn, TOut> func); 

che ... non funziona. TIn ora è necessario essere covariante e TOut controvariante.

Capisco, che non posso usare i tipi generici covarianti come input per i metodi, ma ho pensato che potrei usarli in un tipo generico nidificato che a sua volta specifica la varianza (Func<in T1, out TResult>).

Ho provato a creare un nuovo tipo di delegato con tipi co-/controvarianti e modificare l'interfaccia per accettare un argomento di questo tipo, senza alcun risultato (stesso errore).

public delegate TOut F<in TDlgIn, out TDlgOut>(TDlgIn input); 

public interface IInterface<in TIn, out TOut> { 
    IInterface<TIn, TOut> DoSomethingWithFunc(F<TIn, TOut> func); 
} 

C'è un modo per rendere felice il compilatore? È persino possibile (ad esempio con altri tipi annidati o argomenti generici aggiuntivi)? Se no, perché no?

+0

Non sono sicuro se [questa discussione] (http://stackoverflow.com/a/8100545/11683) sia applicabile, ma potrebbe essere. – GSerg

+0

Penso, questo è relativo a una domanda che ho chiesto qualche tempo fa: http: // stackoverflow.it/questions/6126741/covarianza-contravariance-conundrum-when-using-generic-interface-constraints – MattC

+0

Hai provato a 'delegare TOut F (input TDlgIn) '? Quando si passa in delegati, la contropartita deve essere il contrario. –

risposta

1

Questo non sarebbe sicuro dal momento che si potrebbe quindi usarlo per fare:

public class Id<I, O> : IInterface<I, O> 
{ 
    private Func<I, O> f; 
    public Id(Func<I, O> f) { this.f = f; } 
    public IInterface<I, O> DoSomething(I i) { this.f(i); return this; } 
    public IInterface<I, O> DoSomethingWithFunc(Func<I, O> newF) { 
     this.f = newF; 
     return this; 
    } 
} 

e poi

Func<Animal, string> fa; 
IInterface<object, string> oi = new Id<object, string>(_ => ""); 
Interface<Monkey, string> mi = oi; //safe 
IInterface<Monkey, string> mi2 = mi.DoSomethingWithFunc(fa); 
oi.DoSomething("not an animal!"); 

a questo punto avrete superato un string a un Func<Animal, string>.

0

Hai provato questo?

delegate TOut F<out TDlgIn, in TDlgOut>(TDlgIn input) 

In caso di passaggio di delegati, la contropartita deve essere il contrario. Non so se aiuta. Non ho idea di cosa vorresti probabilmente fare nel metodo.

+0

Sì, questo ovviamente funzionerebbe. Ma voglio che il mio input sia un input e non un input/output scambiato. Non penso che questo farà ciò che mi aspetto. Dovrei provare prima di poter fare una conclusione. Forse fa quello che mi serve, ma è solo molto intuitivo. – knittl

+0

In realtà, non posso usare 'TDlgIn' come parametro, perché è contrassegnato come' out' (co-variant) – knittl

+1

Quando ci pensi abbastanza a lungo, in realtà * è * intuitivo (a parte il fatto che l'intera cosa è oltre ogni intuizione ...). La tua classe riceve un delegato e di solito vuole chiamarlo. Quindi, per passare gli argomenti, deve essere compatibile. Quando si passa in un parametro al delegato, sta andando * out * della classe ed è quindi 'out'. Quello che torni dal delegato sta arrivando * nella * tua classe, quindi è 'in'. –

0
Error CS1961 Invalid variance: The type parameter 'TIn' must be covariantly valid on 'IInterface<TIn, TOut>.DoSomethingWithFunc(Func<TIn, TOut>)'. 'TIn' is contravariant. 

quello che in realtà sta cercando di fare, è che stai passando co-variante di tipo Tout come argomento al metodo 'DoSomethingWithFunc'. Non è possibile, solo i tipi In possono essere passati solo come argomenti, solo come risultati. Nell'esempio, si inserisce TOut come argomento (verrà passato a "DoSomethingWithFunc" poiché TOut è il risultato di Func).

Ci sono un sacco di articoli in tutto il web su di esso (cosa vuol dire essere 'covariantly valida', ma penso che il migliore esplicativo è: https://blogs.msdn.microsoft.com/ericlippert/2009/12/03/exact-rules-for-variance-validity/

Questo significa che si potrebbe, naturalmente put il tuo Func come risultato del metodo nella tua interfaccia

+0

Inoltre, se vuoi invertire i tipi per ingannare il compilatore (che in realtà cambierà solo i tipi di output, quindi probabilmente non ti aiuterà) potresti usare Func > –

+0

usando un Func > sembra interessante , gli darà un colpo. – knittl

Problemi correlati