Question

Je viens d'installer VS2008 et j'ai rencontré un problème qui, j'en suis sûr, peut être résolu avec le logiciel lambda ou les délégués (ou une combinaison!).

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

Le problème est que, parfois, je dois vérifier si la chaîne contient l'une des deux valeurs différentes. Parfois, il peut être nécessaire de vérifier trois valeurs.

Donc, ce que je propose, c'est de changer " ! sb.ToString (). Contains (terminator) " à une fonction qui est transmise à la méthode.

Je pourrais écrire mes différentes fonctions telles que:

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...

Ensuite, lorsque je souhaite comparer trois valeurs différentes, créez un délégué pour l'une de ces fonctions, puis transmettez-le à la méthode ReadData ().

Je suis très désemparé en ce qui concerne les délégués et je ne suis pas sûr que cela semble être le bon endroit pour un lambda, mais quelque chose me dit que oui.

Le code d'appel est le suivant:

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

HasData est identique à ReadData, mais renvoie une valeur booléenne au lieu d'une chaîne (ce que j'aimerais également intégrer à une méthode en utilisant une astuce - mais c'est une question secondaire - n'hésitez pas à y répondre.

Juste pour référence:

     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);
    }
Était-ce utile?

La solution

On dirait que vous recherchez une fonction de prédicat. Au lieu de coder en dur la vérification, prenez un délégué comme paramètre pouvant effectuer la vérification

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

Vous pouvez ensuite créer plusieurs wrappers qui ne font que créer le délégué approprié et le transmettre

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

Vous pouvez même créer un délégué à la volée en fonction d'un nombre arbitraire de terminateurs

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

Autres conseils

Une option serait de surcharger la méthode ReadData () pour prendre un tableau de chaînes contenant les valeurs que vous recherchez. En utilisant une méthode d'extension , vous pouvez étendre Contains () pour prendre une tableau de chaînes.

Votre méthode ReadData () pourrait être:

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'extension de méthode Contains () pourrait être:

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

Cette implémentation élimine le besoin de créer un nouveau prédicat chaque fois que vous avez un nombre différent de chaînes à tester.

Parce que la syntaxe des lambdas est quelque peu étrangère à moi-même (et au reste de mon équipe), je me suis retrouvé avec une solution légèrement différente. Je ne pouvais pas comprendre la syntaxe de .All () lors de la modification de la fonction .Any () ci-dessus.

J'avais aussi besoin d'une fonction .All () pour m'assurer que tous les terminateurs de la liste étaient trouvés. Donc, j'ai fini par aller avec quelque chose comme ce qui suit:

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

Ensuite, le code d'appel ressemble à:

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, ...);
}

Je vais probablement ajouter des contrôles nuls dans les fonctions Has [Any | All], inverser la procédure pendant un certain temps et vérifier la réponse! = null au lieu de dupliquer les paramètres. Cette solution convient à tous mes cas d'utilisation et est assez lisible, je pense. Tant que je fais les petites modifications que je viens de mentionner.

Tout cela met en évidence mon besoin d'apprendre les expressions lambda!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top