这个代码有多糟糕?

时间:2009-10-15 04:32:49

标签: c# .net asp.net graffiticms

好的,我是一名业余程序员,只是写了这篇文章。它完成了工作,但我想知道它有多糟糕,可以做出哪些改进。

[请注意,这是Graffiti CMS的Chalk扩展程序。]

public string PostsAsSlides(PostCollection posts, int PostsPerSlide)
    {
        StringBuilder sb = new StringBuilder();
        decimal slides = Math.Round((decimal)posts.Count / (decimal)PostsPerSlide, 3);
        int NumberOfSlides = Convert.ToInt32(Math.Ceiling(slides));

        for (int i = 0; i < NumberOfSlides; i++ )
        {
            int PostCount = 0;
            sb.Append("<div class=\"slide\">\n");
            foreach (Post post in posts.Skip<Post>(i * PostsPerSlide))
            {
                PostCount += 1;
                string CssClass = "slide-block";

                if (PostCount == 1)
                    CssClass += " first";
                else if (PostCount == PostsPerSlide)
                    CssClass += " last";

                sb.Append(string.Format("<div class=\"{0}\">\n", CssClass));
                sb.Append(string.Format("<a href=\"{0}\" rel=\"prettyPhoto[gallery]\" title=\"{1}\"><img src=\"{2}\" alt=\"{3}\" /></a>\n", post.Custom("Large Image"), post.MetaDescription, post.ImageUrl, post.Title));
                sb.Append(string.Format("<a class=\"button-launch-website\" href=\"{0}\" target=\"_blank\">Launch Website</a>\n", post.Custom("Website Url")));
                sb.Append("</div><!--.slide-block-->\n");

                if (PostCount == PostsPerSlide)
                    break;
            }
            sb.Append("</div><!--.slide-->\n");
        }

        return sb.ToString();
    }

8 个答案:

答案 0 :(得分:12)

使用HtmlTextWriter 代替StringBuilder编写HTML:

StringBuilder sb = new StringBuilder();
using(HtmlTextWriter writer = new HtmlTextWriter(new StringWriter(sb)))
{
    writer.WriteBeginTag("div");
    writer.WriteAttribute("class", "slide");
    //...
}
return sb.ToString();

我们不想使用非结构化编写器来编写结构化数据。

将内循环的主体分解为单独的例程

foreach(...)
{
    WritePost(post, writer);
}

//snip

private void WritePost(Post post, HtmlTextWriter writer)
{
    //write single post
}

这使得它可以测试并且更容易修改。

使用数据结构管理CSS类等内容。

不要使用空格附加额外的类名,并希望所有内容都在最后排成一行,而是根据需要添加和删除List<string>个类名,然后调用:

List<string> cssClasses = new List<string>();

//snip

string.Join(" ", cssClasses.ToArray());

答案 1 :(得分:5)

这不是很好,但我看到的情况要糟糕得多。

假设这是ASP.Net,您是否有理由使用此方法而不是转发器?

编辑:

如果在这种情况下无法使用转发器,我建议使用.Net HTML类来生成HTML而不是使用文本。稍后阅读/调整会更容易一些。

答案 2 :(得分:5)

而不是这个,

        foreach (Post post in posts.Skip<Post>(i * PostsPerSlide))
        {
            // ...
            if (PostCount == PostsPerSlide)
                break;
        }

我会将退出条件移动到循环中,如下所示:(并在您使用时消除不必要的通用参数)

        foreach (Post post in posts.Skip(i * PostsPerSlide).Take(PostsPerSlide))
        {
            // ...
        }

事实上,你的两个嵌套循环可能只能在一个循环中处理。

另外,我更喜欢使用html属性的单引号,因为这些是合法的,不需要转义。

答案 3 :(得分:2)

我看到的情况要糟糕得多,但你可以改善它。

  1. 使用StringBuilder.AppendFormat()
  2. 代替内部使用string.Format()的StringBuilder.Append()
  3. 在它周围添加一些单元测试,以确保它产生你想要的输出,然后重构里面的代码更好。测试将确保您没有破坏任何东西。

答案 4 :(得分:2)

我的第一个想法是,单位测试将非常困难。

我建议将第二个for循环作为一个单独的函数,所以你会有类似的东西:

for (int i = 0; i < NumberOfSlides; i++ )
        {
            int PostCount = 0;
            sb.Append("<div class=\"slide\">\n");
            LoopPosts(posts, i);
...

和LoopPosts:

private void LoopPosts(PostCollection posts, int i) {
...
}

如果你习惯做这样的循环,那么当你需要进行单元测试时,将更容易测试与外循环分开的内循环。

答案 5 :(得分:1)

我说它看起来不错,代码没有严重问题,而且效果很好。但这并不意味着它无法改进。 :)

以下是一些提示:

对于一般浮点运算,您应该使用double而不是Decimal。但是,在这种情况下,您根本不需要任何浮点运算,只需使用整数:

int numberOfSlides = (posts.Count + PostsPerSlide - 1) / PostsPerSlide;

但是,如果您只是在循环中使用单个循环而不是循环中的循环,则根本不需要幻灯片计数。

局部变量的约定是camel case(postCoint)而不是pascal case(PostCount)。尝试使变量名称意义重大,而不是像“sb”那样模糊的缩写。如果变量的范围太小而你真的不需要一个有意义的名称,那么只需要尽可能简单,只需要一个字母而不是缩写。

您可以直接指定文字字符串,而不是连接字符串“slide block”和“first”。这样就可以用简单的赋值替换对String.Concat的调用。 (这与过早优化有关,但另一方面,字符串连接需要大约50倍。)

您可以在StringBuilder上使用.AppendFormat(...)代替.Append(String.Format(...))。在这种情况下我会坚持使用Append,因为实际上没有什么需要格式化,你只是连接字符串。

所以,我会写这样的方法:

public string PostsAsSlides(PostCollection posts, int postsPerSlide) {
   StringBuilder builder = new StringBuilder();
   int postCount = 0;
   foreach (Post post in posts) {
      postCount++;

      string cssClass;
      if (postCount == 1) {
         builder.Append("<div class=\"slide\">\n");
         cssClass = "slide-block first";
      } else if (postCount == postsPerSlide) {
         cssClass = "slide-block last";
         postCount = 0;
      } else {
         cssClass = "slide-block";
      }

      builder.Append("<div class=\"").Append(cssClass).Append("\">\n")
         .Append("<a href=\"").Append(post.Custom("Large Image"))
         .Append("\" rel=\"prettyPhoto[gallery]\" title=\"")
         .Append(post.MetaDescription).Append("\"><img src=\"")
         .Append(post.ImageUrl).Append("\" alt=\"").Append(post.Title)
         .Append("\" /></a>\n")
         .Append("<a class=\"button-launch-website\" href=\"")
         .Append(post.Custom("Website Url"))
         .Append("\" target=\"_blank\">Launch Website</a>\n")
         .Append("</div><!--.slide-block-->\n");

      if (postCount == 0) {
         builder.Append("</div><!--.slide-->\n");
      }

   }
   return builder.ToString();
}

答案 6 :(得分:0)

如果帖子的定义存在会有所帮助,但总的来说,我会说在代码中生成HTML并不是Asp.Net的方法。

因为它被标记为.Net特别......

要显示集合中的项目列表,最好查看其中一个数据控件(转发器,数据列表等)并学习如何正确使用它们。

http://quickstarts.asp.net/QuickStartv20/aspnet/doc/ctrlref/data/default.aspx

答案 7 :(得分:0)

您可以采取的另一项措施:用sb.Append(string.Format(...))替换sb.AppendFormat(...)来电。