Рефакторинг с лямбдами и делегатами - PullRequest
1 голос
/ 01 мая 2009

Я только что установил 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...

Затем, когда я хочу сравнить с 3 различными значениями, создайте делегат для одной из этих функций, а затем передайте его методу 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);
    }

Ответы [ 3 ]

4 голосов
/ 01 мая 2009

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

    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());
}
1 голос
/ 01 мая 2009

Один из вариантов - перегрузить метод 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;
}

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

0 голосов
/ 03 мая 2009

Поскольку синтаксис лямбда-символов несколько чужд для меня (и для остальной части моей команды), я в итоге выбрал немного другое решение. Я не мог понять синтаксис .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, ...);
}

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

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

...