将switch语句映射到数据类

时间:2009-10-24 13:14:14

标签: c# .net refactoring switch-statement

这似乎在我的代码中出现了很多,我想知道是否有某种方法可以删除switch语句,或者是否有更优雅的方式来执行此操作?

    public class MetaData
{
    public string AlbumArtist { get; set; }
    public string AlbumTitle { get; set; }
    public string Year { get; set; }
    public string SongTitle { get; set; }


    public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
    {
        var metaData = new MetaData();

        foreach (var frame in textFrames)
        {
            switch (frame.Descriptor.ID)
            {
                case "TPE1":
                    metaData.AlbumArtist = frame.Content;
                    break;

                case "TALB":
                    metaData.AlbumTitle = frame.Content;
                    break;

                case "TIT2":
                    metaData.SongTitle = frame.Content;
                    break;

                case "TYER":
                    metaData.Year = frame.Content;
                    break;
            }
        }

        return metaData;
    }
}

6 个答案:

答案 0 :(得分:3)

这与面向对象的方法有关。摆脱if或case的常用方法是使用标准和效果的查找表。还有其他技术使用同样的想法,例如数据导向编程(http://en.wikipedia.org/wiki/Data-directed_programming)和调度表(http://en.wikipedia.org/wiki/Dispatch_table)。许多语言实现使用类型调度表来实现虚方法调用。

查找表可以是一个用lambda函数填充的哈希表,如下所示:

Dictionary<string, Func<MetaData, string, string>> lookup = new Dictionary<string, Func<MetaData, string, string>>();
lookup["TPE1"] = (m, v) => m.AlbumArtist = v;
lookup["TALB"] = (m, v) => m.AlbumTitle = v;
lookup["TIT2"] = (m, v) => m.SongTitle = v;
lookup["TYER"] = (m, v) => m.Year = v;

然后在循环中将metaData字段分配为:

lookup[frame.Descriptor.ID](metaData, frame.Content);

答案 1 :(得分:1)

您可能希望了解如何实施策略模式。 DimeCasts.Net有一个很棒的视频教程可能会有所帮助。

答案 2 :(得分:1)

我很想提出策略模式但你可能需要稍微改变一下。考虑在TextFrame类中编写一个方法,让我们称之为putContent(MetaData)。

然后创建TextFrame的子类,每个子类表示不同的Frame类型。每个子类都将覆盖putContent(元数据)方法并执行其适当的逻辑。

TPE1的伪码示例:

 Metadata putContent(MetaData md){
       md.AlbumArtist = Content;
       return md;
 }

您将MetaData代码更改为:

var metaData = new MetaData();

    foreach (var frame in textFrames)
    {
           metaData = frame.putContent(metaData);
    }
 return metaData;

当然,要创建TextFrames,他们自己将需要一个工厂,所以这不是故事的结尾。

答案 3 :(得分:1)

看起来你知道手头的类型是什么(使用开关)所以为什么不只是根据需要检索值,而不用for开关。

示例是在使用哈希表时,您知道哪些字段可用,只需使用字段。

ig你不确定该字段是否可用,如果列表包含值,则简单的测试就足够了。

如果列表具有值,您甚至可以编写辅助函数来检查并返回值。

答案 4 :(得分:1)

从您的代码中我得出结论IEnumerable&lt; TextFrame&gt;一共有4名成员 所以你可以写(没试过,所以检查语法):

public static MetaData CreateMetaDataFrom(IEnumerable<TextFrame> textFrames)
{
    return new MetaData()
    {
        metaData.AlbumArtist = textFrames.Where(frame => frame.Descriptor.ID = "TPE1").SingleOrDefault().Content,
        metaData.AlbumTitle = textFrames.Where(frame => frame.Descriptor.ID = "TALB").SingleOrDefault().Content, 
        metaData.SongTitle = textFrames.Where(frame => frame.Descriptor.ID = "TIT2").SingleOrDefault().Content;
        metaData.Year = textFrames.Where(frame => frame.Descriptor.ID = "TYER").SingleOrDefault().Content;
    };
}

答案 5 :(得分:0)

你真正拥有的是一个四向制定者。这里的规范重构是“用显式方法替换参数”(Martin Fowler的重构的p285)。他给出的Java示例正在改变:

void setValue(String name, int value) {
  if (name.equals("height")) {
    _height = value;
    return;
  }
  if (name.equals("width")) {
    _width = value;
    return;
  }
}

为:

void setHeight(int arg) {
  _height = arg;
}

void setWidth(int arg) {
  _width = arg;
}

假设CreateMetaDataFrom()的调用者知道它传入了什么,你可以跳过switch/case并使用这些属性的实际setter。

相关问题