Domanda

Ho appena installato VS2008 e ho riscontrato un problema che sono sicuro che possa essere risolto con lambda o delegati (o una combinazione!).

    private string ReadData(TcpClient s, string terminator)
    {
        // Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
        var sb = new StringBuilder();
        do
        {
            var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
            sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
        } while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));

        return sb.ToString();
    }

Il problema è che a volte devo verificare se la stringa contiene uno di due valori diversi. A volte potrei aver bisogno di controllarlo per tre valori.

Quindi quello che propongo è cambiare " !. Sb.ToString () Contiene (Terminator) " a una funzione che viene passata nel metodo

Potrei scrivere le mie diverse funzioni come:

private bool compare1(string s, string t) {
    return s.contains(t)
}

private bool compare2(string s, string t1, string t2) {
    return (s.compare(t1) or s.compare(t2)
}

// etc...

Quindi, quando voglio confrontare 3 diversi valori, creare un delegato a una di queste funzioni, quindi passarlo al metodo ReadData ().

Sono molto all'oscuro quando si tratta di delegati, e non sono sicuro se questo sembra il posto giusto per un lambda ma qualcosa mi sta dicendo che lo è.

Il codice chiamante è questo:

            // Enter username .
        if (HasData(s,"login:"))
            SendData(s, switchUser + TelnetHelper.CRLF);

HasData è identico a ReadData, ma restituisce un valore bool invece che una stringa (che vorrei anche includere in un metodo usando qualche trucco - ma questa è una domanda secondaria - sentiti libero di rispondere però.

Solo per riferimento:

     private bool HasData(TcpClient s, string terminator)
    {
        // Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
        var sb = new StringBuilder();
        do
        {
            var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
            sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
        } while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminator));

        return sb.ToString().Contains(terminator);
    }
È stato utile?

Soluzione

Sembra che tu stia cercando una funzione predicata. Invece di codificare a fondo il controllo, prendi un delegato come parametro che può fare il controllo

    private string ReadData(TcpClient s, Func<string,bool> predicate)
    {
        // Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
        var sb = new StringBuilder();
        do
        {
            var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
            sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
        } while (s.GetStream().DataAvailable && !predicate(sb));

        return sb.ToString();
    }

Quindi puoi creare diversi wrapper che creano semplicemente il delegato appropriato e lo passano

public bool HasData(TcpClient c, string terminator) {
  return HasData(c, (s) => s.Contains(terminator));
}

public bool HasData(TcpClient c, string t1, string t2) {
  return HasData(c, (s) => s.Contains(t1) || s.Contains(t2));
}

Puoi persino creare un delegato al volo in base al numero arbitrario di terminatori

public bool HasData(TcpClient c, params string[] terminatorList) {
  return HasData(c, (s) => terminatorList.Where(x => s.Contains(x)).Any());
}

Altri suggerimenti

Un'opzione sarebbe quella di sovraccaricare il metodo ReadData () per acquisire una matrice di stringhe contenente i valori che si stanno verificando. Utilizzando un metodo di estensione , puoi estendere Contains () per prendere un array di stringhe.

Il tuo metodo ReadData () potrebbe essere:

private string ReadData(TcpClient s, string[] terminators) {
    // Reads a byte steam into a string builder until either data is unavailable or the terminator has not been reached
    var sb = new StringBuilder();
    do
    {
        var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
        sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
    } while (s.GetStream().DataAvailable && !sb.ToString().Contains(terminators));

    return sb.ToString();
}

L'estensione del metodo Contains () potrebbe essere:

public static bool Contains ( this String str , String[] testValues )
{
    foreach ( var value in testValues )
    {
        if ( str.Contains( value ) )
            return true;
    }
    return false;
}

Questa implementazione elimina la necessità di creare un nuovo predicato ogni volta che si dispone di un numero diverso di stringhe da testare.

Poiché la sintassi degli lambda è in qualche modo estranea a me stessa (e al resto della mia squadra), ho finito con una soluzione leggermente diversa. Non sono riuscito a capire la sintassi di .All () quando modificato dalla funzione .Any () sopra.

Avevo bisogno anche di una funzione .All (), per assicurarmi di trovare tutti i terminatori nell'elenco. Quindi ho finito con qualcosa di simile al seguente:

delegate bool Predicate (string s, params [] string terminators);

bool HasAll(string s, params string [] terminators) {
    foreach (var t in terminators) {
       if (!s.contains(t)) return false;
    }
    return true;
}

bool HasAny(string s, params string [] terminators) {
    foreach (var t in terminators) {
        if (s.contains(t)) return true;
    }
    return false;
}
// Just looking now, I could also pass in a bool to switch between the two and remove one of these functions. But this is fairly clear


string ReadData(TcpClient sock, Function predicate, params [] string terminators) {
    var sb = new StringBuilder();
    do
    {  
        var numBytesRead = s.GetStream().Read(byteBuff, 0, byteBuff.Length);
        sb.AppendFormat("{0}", Encoding.ASCII.GetString(byteBuff, 0, numBytesRead));
    } while (s.GetStream().DataAvailable && !predicate(sb.ToString(), terminators);

    return sb.ToString();
}

Quindi il codice chiamante appare come:

private void someFunc() 
{
    Predicate any = new Predicate(HasAny);
    Predicate all = new Predicate(HasAll);
    String response;

    // Check all strings exist
    response = ReadData(this.sock, all, "(", ")", "->")
    if (all(response, "(", ")", "->")
        SendData(this.sock, ...);

    // Check any string exists
    response = ReadData(this.sock, any, "Hi", "Hey", "Hello");
    if (any(response, "Hi", "Hey", "Hello"))
       SendData(this.sock, ...);
}

Probabilmente aggiungerò i controlli null nelle funzioni Has [Any | All], invertirò il do..while per un po 'e controllerò semplicemente response! = null invece di duplicare i parametri. Questa soluzione si adatta a tutti i miei casi d'uso ed è abbastanza leggibile dall'uomo, credo. Fintanto che apporto le piccole modifiche di cui ho appena parlato.

Tutta questa faccenda sottolinea per me il mio bisogno di imparare le espressioni lambda!

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