【问题标题】:c# foreach loop written poorlyc# foreach 循环写得不好
【发布时间】:2012-06-25 16:27:34
【问题描述】:

这个 foreach 循环在测试时工作正常,只返回 5 行数据,但我很清楚它的编写有多糟糕,有没有更好的方法,可能使用 stringbuilder 更有效地重写它?

               SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["connstring"].ConnectionString);
    SqlCommand comm = new SqlCommand("SELECT Title, StartDate FROM tblEvents JOIN eo_UserEventWatch ON eo_UserEventWatch.EventID=tblEvents.ID WHERE eo_UserEventWatch.UserID = @GUID ;", conn);
    comm.Parameters.AddWithValue("GUID", userID);
    conn.Open();
    SqlDataAdapter da = new SqlDataAdapter(comm);
    DataTable dt = new DataTable();
    da.Fill(dt);
    string result ="{ \"event\" :[";
    foreach (DataRow dr in dt.Rows)
    {
        result += "{\"title\" : \"" + dr[0].ToString() + "\" , \"start\" : \"" + dr[1].ToString() +"\"} ,";
    }
    result = result.TrimEnd(',');
    result += "] }";
    return result;

【问题讨论】:

  • StringBuilder 更高效,但更冗长。
  • 不要使用字符串操作构建Json。使用 Json.Net、JavaScriptSerializer、DataContractJsonSerializer 等 json 解析器...否则,很容易得到无效的 json
  • 您应该使用 JSON 库来构建您的 JSON。
  • @TheGeekYouNeed 你测量了吗?该行被重写为 one 调用以连接。与最多四个追加调用相比,它不太可能特别慢
  • 正如@JesseC.Slicer 所说,您确实需要处理连接、命令和数据适配器对象。要么将它们包装在 using 块中,要么关闭它们并将整个块包装在 try/catch/finally 中,然后在 finally 块中处理它们。

标签: c# asp.net sqlcommand


【解决方案1】:

是的,使用StringBuilder 会更有效:

StringBuilder builder = new StringBuilder("{ \"event\" :[");
foreach (DataRow row in dt.Rows)
{
    // Alternatively, use AppendFormat
    builder.Append("{\"title\" :\"");
           .Append(row[0])
           .Append("\", \"start\" : \"")
           .Append(row[1])
           .Append("\"} ,");
}
if (builder[builder.Length - 1] == ',')
{
    builder.Length -= 1;
}
builder.Append("] }");
string result = builder.ToString();

然而,它仍然不是漂亮 代码——因为你已经得到了所有可怕的文字 JSON。我建议使用 Json.NET 或其他 JSON 库。那时您可能可以使用 LINQ,例如

var result = new { event = dt.AsEnumerable()
                             .Select(r => new { 
                                 title = r.Field<string>(0),
                                 start = r.Field<DateTime>(1))
                             .ToArray() };
// Or whatever, depending on the library you use
var json = JsonSerializer.ToJson(result);

除此之外,现在您不必担心起始值的格式,或者标题是否包含引号等。

(编辑:如前所述,您绝对应该拥有用于 SQL 连接等的 using 语句。这超出了问题的重点,这就是为什么我在这里没有提到它。)

【讨论】:

  • (dt.Rows 中的DataRow 行) -> .Append(dr[0]) ,不过它很酷,你必须快点,它有效,当它让我接受时我会接受
  • 我不打算投赞成票,但我不能投反对票 关于使用适当的 JSON 库的新内容...
  • 还有 "if (builder.EndsWith(","))" - System.Text.StringBuilder 中没有 .EndsWith ??
  • 我从不喜欢从字符串生成器中追溯删除最后一个分隔符的技巧。尽管它是正确的,但它似乎并不正确。我喜欢将连接循环封装到扩展方法中。我知道这是专门的演示代码。
  • @ScottSelby 作为奖励,如果你在我手下开发并使用 Json.NET 编写代码,那么我不会让你重写的可能性很多它;-)
【解决方案2】:

下面是您将如何使用 StringBuilder

StringBuilder sb = new StringBuilder();
sb.Append("{ \"event\" :[");

foreach(DataRow dr in dt.Rows)
{
    sb.Append("{\"title\" : \"");
    sb.Append(dr[0].ToString());
    sb.Append("\" , \"start\" : \"");
    sb.Append(dr[1].ToString());
    sb.Append("\"} ,");
}

sb.Remove(sb.ToString().Length - 1, 1);
sb.Append("] }");

return sb.ToString();

要完全做到这一点,我会使用 JavaScriptSerializer 之类的东西(只是一个示例):

var stuff= (from DataRow dr in dt.AsEnumerable()
            select new {
                 DataItem1 = (string)dr[0];
                 DataItem2 = (string)dr[1];
            });

JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(stuff);

【讨论】:

  • 如果没有行怎么办?现在我们只有 more 无效的 JSON。这就是为什么字符串构建很糟糕的原因。
  • 同意 pst -- 我只是在展示如何使用 StringBuilder
  • +1 用于展示 JavaScriptSerializer 的使用...虽然我更愿意使用 Json.NET(如果可用)。
【解决方案3】:

string.format 在内部使用 StringBuilder。

Is String.Format as efficient as StringBuilder

public static string GetResult()
        {
            int userId = 0;
            string connectionString = ConfigurationManager.ConnectionStrings["connstring"].ConnectionString;
            string statement = "SELECT Title, StartDate FROM tblEvents JOIN eo_UserEventWatch ON eo_UserEventWatch.EventID=tblEvents.ID WHERE eo_UserEventWatch.UserID = @GUID";

            using (var con = new SqlConnection(connectionString))          
            using (var cmd = new SqlCommand(statement, con))
            {
                con.Open();
                cmd.Parameters.AddWithValue("GUID", userId);
                using (var dataAdapter = new SqlDataAdapter(cmd))
                {
                    DataTable dt = new DataTable();
                    dataAdapter.Fill(dt);
                    string result = "{ \"event\" :[";
                    foreach (DataRow dr in dt.Rows)
                    {
                        result += string.Format(@"{\title\ : \{0}\ , \start\ : \{1}\} ,", dr[0].ToString(), dr[1].ToString());
                    }
                    result = result.TrimEnd(',');
                    result += "] }";
                    return result;
                }
            }          
        }

【讨论】:

  • 所以这增加了using ...但根本不解决字符串构建问题。并且没有说明给它上下文/理由。
  • 你仍然在一个循环中连接,这是低效的位。见pobox.com/~skeet/csharp/stringbuilder.html
猜你喜欢
  • 2012-01-14
  • 2022-01-04
  • 2020-02-19
  • 2010-12-27
  • 2013-01-09
  • 2016-07-05
  • 2010-11-16
相关资源
最近更新 更多