Вопрос

Я только что установил VS2008 и столкнулся с проблемой, которую, я уверен, можно решить с помощью лямбда-выражений или делегатов (или их комбинации!).

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

Проблема в том, что иногда мне нужно проверить, содержит ли строка одно из двух разных значений.Иногда мне может потребоваться проверить его по трем значениям.

Итак, я предлагаю изменить « !sb.ToString().Contains(terminator)» на функцию, которая передается в метод.

Я мог бы написать свои различные функции, такие как:

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

Затем, когда я хочу сравнить три разных значения, создайте делегат для одной из этих функций, а затем передайте его методу ReadData().

Я очень невежественен, когда дело касается делегатов, и не уверен, что это подходящее место для лямбды, но что-то мне подсказывает, что это так.

Код вызова такой:

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

HasData идентичен ReadData, но возвращает bool вместо строки (который я также хотел бы объединить в один метод, используя некоторую хитрость - но это второстепенный вопрос - хотя не стесняйтесь на него отвечать.

Просто для справки:

     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);
    }
Это было полезно?

Решение

Похоже, вы ищете функцию-предикат.Вместо жесткого кодирования проверки возьмите в качестве параметра делегата, который сможет выполнить проверку.

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

Затем вы можете создать несколько оболочек, которые просто создают соответствующий делегат и передают его.

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

Вы даже можете создать делегат на лету на основе произвольного количества терминаторов.

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

Другие советы

Одним из вариантов может быть перегрузка метода ReadData() для получения массива строк, содержащего проверяемые значения.Используя метод расширения, вы можете расширить contains(), чтобы он принимал массив строк.

Ваш метод ReadData() может быть:

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

Расширение метода contains() может быть:

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

Эта реализация устраняет необходимость создавать новый предикат каждый раз, когда у вас есть различное количество строк для проверки.

Поскольку синтаксис лямбда-выражений мне (и остальной части моей команды) несколько чужд, в итоге я выбрал немного другое решение.Я не смог понять синтаксис .All() при изменении функции .Any() выше.

Мне также нужна была функция .All(), чтобы гарантировать, что все терминаторы в списке найдены.В итоге я пришел к чему-то вроде следующего:

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

Тогда вызывающий код будет выглядеть так:

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

Я, вероятно, добавлю проверки на null в функции Has[Any|All], изменю do.. while на while и просто проверю ответ != null вместо дублирования параметров.Это решение подходит для всех моих вариантов использования и, я думаю, вполне удобочитаемо для человека.Пока я внесу небольшие изменения, о которых упомянул чуть выше.

Однако для меня все это подчеркивает необходимость изучения лямбда-выражений!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top