Pergunta

Acabei de instalar o VS2008 e tenho um problema que tenho certeza de que pode ser resolvido com o Lambda ou os delegados (ou uma combinação!).

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

O problema é que às vezes preciso verificar se a string contém um dos dois valores diferentes. Às vezes, posso precisar verificar por três valores.

Então, o que proponho é mudar "! SB.ToString ().

Eu poderia escrever minhas diferentes funções, como:

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

Então, quando eu quiser comparar com três valores diferentes, crie um delegado com uma dessas funções e passe isso para o método readdata ().

Não tenho muito noção quando se trata de delegados e não tenho certeza se esse parece o lugar certo para um lambda, mas algo está me dizendo que é.

O código de chamada é o seguinte:

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

O hasdata é idêntico ao ReadData, mas retorna um bool em vez de uma string (que eu também gostaria de levar em consideração um método usando alguns truques - mas essa é uma pergunta secundária - fique à vontade para responder isso.

Somente para referência:

     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);
    }
Foi útil?

Solução

Parece que você está procurando uma função de predicado. Em vez de codificar o cheque, pegue um delegado como um parâmetro do que pode fazer o cheque

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

Em seguida, você pode criar vários invólucros que apenas criam o delegado apropriado e passam por

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

Você pode até construir um delegado em tempo real com base no número arbitrário de terminadores

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

Outras dicas

Uma opção seria sobrecarregar o método readdata () para pegar uma matriz de string contendo os valores para os quais você está verificando. Usando um Método de extensão, você pode estender contém () para pegar uma matriz de string.

Seu método readData () pode ser:

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

A extensão do método contém () pode ser:

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

Essa implementação elimina a necessidade de criar um novo predicado cada vez que você tem um número diferente de strings para testar.

Como a sintaxe dos Lambdas é um pouco estranha para mim mesma (e para o resto da minha equipe), acabei indo com uma solução ligeiramente diferente. Não consegui descobrir a sintaxe de .All () quando modificado a partir da função .any () acima.

Eu também precisava de uma função .All () para garantir que todos os terminadores da lista fossem encontrados. Então eu acabei indo com algo como o seguinte:

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

Então o código de chamada parece:

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

Provavelmente, adicionarei verificações nulas às funções [de qualquer | todas], revertem o faça ... por um tempo e apenas verifique a resposta! = NULL em vez de duplicar os parâmetros. Essas soluções se adaptam a todos os meus casos de uso e são bastante legíveis humanos, eu acho. Contanto que eu faça as pequenas alterações que mencionei logo acima.

Essa coisa toda destaca para mim minha necessidade de aprender expressões Lambda!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top