Domanda

Sono ancora nuovo ai delegati e ho giocato con il Data Access Layer delegato a base descritto nel film di Steven John Metsker "Design Patterns in C #" libro (un'ottima lettura!). Esso definisce il delegato l'accesso ai dati in questo modo:

public delegate object BorrowReader(IDataReader reader);

Il risultato dell'utilizzo di questo è il codice che assomiglia a uno di questi:

var result = Foo.Bar(new BorrowReader(DoFooBarMagic));
var result = Foo.Bar(DoFooBarMagic);

Tuttavia, dal momento che tipo di ritorno del delegato è "oggetto", è necessario lanciare per ottenere ciò il metodo ( "DoFooBarMagic" in questo esempio) restituisce davvero. Quindi, se "DoFooBarMagic" restituisce List, avresti bisogno di fare qualcosa di simile:

var result = Foo.Bar(DoFooBarMagic) as List<string>;

Quello che mi piacerebbe è quello di essere in grado di saltare il cast e hanno il tipo di ritorno del delegato dedotto dal tipo di ritorno del metodo delegato. Il mio pensiero era forse c'è un modo per utilizzare un parametro Type per inferenza il tipo di ritorno. Qualcosa di simile a uno di questi:

public delegate T BorrowReader<T>(IDataReader reader);
List<string> result = Foo.Bar(new BorrowReader(DoFooBarMagic)); 
//Look, Ma, no cast!
var result2 = Foo.Bar(DoFooBarMagic);

Se il tipo di ritorno viene dedotto dal tipo di ritorno del metodo delegato, ma che non sembra funzionare. Invece quello che dovete fare questo:

public delegate T BorrowReader<T>(IDataReader reader);
var result = Foo.Bar(new BorrowReader<List<string>>(DoFooBarMagic));

che difficilmente sembra migliore rispetto al cast.

Quindi non v'è un modo per dedurre il tipo di ritorno del delegato dal tipo di ritorno del metodo delegato?

Modifica per aggiungere: Posso cambiare la firma di Foo.Bar se necessario. La firma corrente è essenzialmente questo:

public static T Bar<T>(string sprocName,
                       DbParameter[] params, 
                       BorrowReader<T> borrower);

Nota: che la firma è il risultato dello stato attuale, che sta utilizzando questa definizione delegato:

public delegate T BorrowReader<T>(IDataReader reader);
È stato utile?

Soluzione

Come su:

public static T Bar2<T>(Func<IDataReader,T> func) where T : class
{
    BorrowReader borrower = new BorrowReader(func);
    return (T) Foo.Bar(borrower);
}

Lo so che è ancora facendo il cast in ogni caso, che è brutto, ma dovrebbe funzionare. (Ho inizialmente pensato che si potrebbe ottenere via con una conversione implicita da func, ma a quanto pare no. Almeno non prima di C # 4.0.)

Naturalmente, se è possibile modificare la firma di Foo.Bar ad essere generico, stai ridendo ...

EDIT: Per rispondere il commento:. Se la firma del metodo è cambiato a prendere un delegato generico, per esempio

public static T Bar<T>(Func<IDataReader, T> func)

allora il codice chiamante può quasi solo essere:

var result = Foo.Bar(DoFooBarMagic);

Purtroppo, inferenza di tipo non funziona con i gruppi di metodo quindi bisogna utilizzare:

Func<IDataReader, List<string>> func = DoFooBarMagic;
var result = Foo.Bar(func);

o (più bello, se un po ' meno efficiente)

var result = Foo.Bar(reader => DoFooBarMagic(reader));

Quindi, hai ragione - questa risposta non ha quasi mai il PO esattamente quello che era necessario, ma presumibilmente è venuto abbastanza vicino per ottenere l'accettazione. Speriamo che questo modifica è utile per spiegare il resto:)

Altri suggerimenti

E 'brutto, ma è possibile utilizzare un parametro out. Dal mio enum parser:

public static T Parse<T>(string value)
{
    // return a value of type T
}

public static void Parse<T>(string value, out T eValue)
{
    // do something and set the out parameter
}

// now can be called via either
SomeEnum blah = Enums.Parse<SomeEnum>("blah");

// OR
SomeEnum blah;
Enums.Parse("blah", out blah);

Il secondo sarà dedurre il tipo, ma come ho detto, è brutto.

Non credo ci sia un modo intorno ad esso. A non meno importante nel modo in cui proporli. Il problema principale è che dal momento che deve essere infered al momento della compilazione, se non si dispone di un tipo di ritorno esplicito, quindi il compilatore può in realtà non dedurre il tipo di ritorno.

Non utilizzare farmaci generici. L'inferenza dei tipi è un evento in fase di compilazione. Una soluzione sarebbe quella di avere un delegato generico come hai postato. Il delegato generico sarebbe più veloce di un cast.

// if BorrowReader is generic...
public delegate T BorrowReader<T>(IDataReader reader);

public class Foo
{
    // ... and Foo.Bar() is also generic
    public static T Bar<T>(BorrowReader<T> borrower) { ... }

    public void SomeMethod()
    {
        // this does *not* work (compiler needs more help)
        var result1 = Foo.Bar(DoFooBarMagic);

        // but instead of this (which works)
        var result2 = Foo.Bar(new BorrowReader<List<string>>(DoFooBarMagic));

        // you can do this (also works)
        // which emits the same IL, anyway
        var result3 = Foo.Bar<List<string>>(DoFooBarMagic);
    }
}

Mi aspetto F # può fare questo. Non sono un esperto, ma lo fa F # tipo impiego di ritorno inferenza di tipo ( dettagli su MSDN ).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top