使用Lambda和Delegates进行重构

时间:2009-05-01 02:20:59

标签: c# .net refactoring delegates lambda

我刚刚安装了VS2008并遇到了一个问题,我确信可以用lambda或代理(或组合!)来解决。

    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()方法。

对于代表们来说,我很无能为力,而且我不确定这对于一个lambda来说是不是正确的地方,但有些东西告诉我它。

调用代码是这样的:

            // 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 个答案:

答案 0 :(得分:4)

听起来你正在寻找一个谓词函数。不要对支票进行硬编码,而是将代理作为参数,而不是检查

    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 :(得分:1)

一个选项是重载ReadData()方法以获取包含您要检查的值的字符串数组。使用extension method,您可以扩展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;
}

每次有不同数量的字符串需要测试时,此实现消除了创建新谓词的需要。

答案 2 :(得分:0)

因为lambdas的语法对我自己(以及我的团队的其他成员)来说有点陌生,所以我最终得到了一个稍微不同的解决方案。从上面的.Any()函数修改后,我无法弄清楚.All()的语法。

我还需要一个.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..while转换为while,然后检查响应!= null而不是复制参数。这个解决方案适合我所有的用例,我认为它具有相当的人性化。只要我做了上面提到的小改动。

这一切都让我想到了学习lambda表达式的必要性!