2016-09-27 64 views
2

喂,伙计们,这是我的第一个问题,我希望这是好\ O/重构大开关的情况下设置属性

所以,首先它是保持在一个插座接收数据,第一次C#应用程序一个连接到服务器,我让我得到这个大字符串(超过100场)的请求,对本例的缘故,我将使它更小:

0:7:1:Augusto:2:Question:3:Stackoverflow:4:Question:5:201609262219:6:stuff:7:stuff:..:100:! 

此后,我收到这个大串,我将只收到更新,例如:

0:7:3:Changed Topic:4:Doubt:5:2016092710:100:! 

字段100将始终出现,因为它定义了字符串终止符。
当我收到这个大串我必须建立一个对象的问题,这个大串总是会导致一个问题的对象,所以我们有:

public class Question 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string Action { get; set; } 
    public string Topic { get; set; } 
    public string Body { get; set; } 
    public string Time { get; set; } 
    ... 
    public string 99 { get; set; } 
} 

要构建问题对象我这样做:

public void Process(string data) 
{ 
    Question question = new Question(); 
    String[] fields = data.split(“:”); 

    for(int i = 0; i < fields.Length; i +=2) { 
     Switch (fields[i]) { 
      case "0": 
       question.Id = fields[i+1] 
        break; 

      case "1": 
       question.Name = fields[i+1] 
        break; 

      case "2": 
       question.Action = fields[i+1] 
        break; 
      case "3": 
       question.Topic = fields[i+1] 
        break; 

      case "4": 
       question.Body = fields[i+1] 
        break; 
      case "5": 
       question.Time = fields[i+1] 
        break; 
       ... 
      case "99": 
        Question.99 = fields[i+1] 
         break; 

     } 
    } 
} 

我研究了通过stackoverflow的答案,但他们不同于我试图实现的。
我不认为使用策略模式或命令模式是值得的,因为逻辑很简单,使用这种模式会增加太多的复杂性。
我不确定Dictionary<string,Func<string>>是否能解决我的问题,我认为它与女巫的情况一样。
我也想过使用反射(setValue),但我认为它会很慢,我真的需要这个东西飞。

这里的问题是,我只是将一个值赋予对象,我不想做这个大开关的情况。我认为我创建对象的方式有些不对,但我不能找到不同的解决方案。

还有一个问题,你们认为string.Split()会成为我的代码瓶颈吗?因为我将Process(数据)方法称为一堆时间,例如每秒100次。

谢谢!

---更新
固定样本和案例“5”。

+0

你能修好你的样品吗?字符串'0:7:3:改变话题4:疑问:5:2016092710:19:100:!'显然是无效的。 – Enigmativity

+0

固定为0:7:3:已更改主题:4:疑问:5:2016092710:100 :! –

+1

将事情分解到更小的可管理类中并不会让事情变得复杂。如果有的话,试图阅读和理解switch语句如何工作超过99个不同的潜在案例很难遵循和难以理解。我为IRC客户端做了这样的事情,并且实施了一个适当的模式来解决这个问题。有模式可以清理事物。通常,如果您发现模式复杂,您可能没有以正确的方式使用它。 –

回答

2

我认为你担心在这种情况下不成熟的优化。一般的经验法则是 - 除非性能问题实际可衡量,否则不要优化。

考虑到这一点,这个问题可以在一个相当优雅的庄园中解决。我将使用一点缓存反射,这样我只需拉动PropertyInfo及其自定义属性一次。所以你会看到初始连接的性能开销很小。在这一点上,这是非常快的。

因为我调用方法Process(数据)一堆时间,就像每秒100次。

我在一台Windows 10虚拟机中运行这个程序,它有4个内核和8个RAM。我运行了一个测试,对包含100个值的字符串进行了10,000次迭代(没有使用缩短的字符串),并且我看到每个字符串的平均解析时间为516.32 ,刻度为(每1ms为10,000个刻度)。考虑到您的要求是1秒内100个字符串,这对于您的吞吐量应该足够快。在1秒的时间内,我能够解析出超过20,000的数据。每秒解析数将最终高于20,000 I处理,因为每次使用整个100个元素的字符串而不是更小的更新字符串。

长时间运行可能会有一些GC压力,我必须运行测试才能看到。这可以通过引入一对对象池来优化,以减少分配。无论采用何种方法,您都会遇到此问题,仅仅因为您必须将1个字符串解析为多个值,所以此解决方案无法解决此问题。

让我们从我的字符串开始,它包含100个元素。

0:FizBam Foo Bar:1:FizBam Foo Bar:2:FizBam Foo Bar:3:FizBam Foo Bar:4:FizBam Foo Bar:5:FizBam Foo Bar:6:FizBam Foo Bar:7:FizBam Foo Bar:8:FizBam Foo Bar:9:FizBam Foo Bar:10:FizBam Foo Bar:11:FizBam Foo Bar:12:FizBam Foo Bar:13:FizBam Foo Bar:14:FizBam Foo Bar:15:FizBam Foo Bar:16:FizBam Foo Bar:17:FizBam Foo Bar:18:FizBam Foo Bar:19:FizBam Foo Bar:20:FizBam Foo Bar:21:FizBam Foo Bar:22:FizBam Foo Bar:23:FizBam Foo Bar:24:FizBam Foo Bar:25:FizBam Foo Bar:26:FizBam Foo Bar:27:FizBam Foo Bar:28:FizBam Foo Bar:29:FizBam Foo Bar:30:FizBam Foo Bar:31:FizBam Foo Bar:32:FizBam Foo Bar:33:FizBam Foo Bar:34:FizBam Foo Bar:35:FizBam Foo Bar:36:FizBam Foo Bar:37:FizBam Foo Bar:38:FizBam Foo Bar:39:FizBam Foo Bar:40:FizBam Foo Bar:41:FizBam Foo Bar:42:FizBam Foo Bar:43:FizBam Foo Bar:44:FizBam Foo Bar:45:FizBam Foo Bar:46:FizBam Foo Bar:47:FizBam Foo Bar:48:FizBam Foo Bar:49:FizBam Foo Bar:50:FizBam Foo Bar:51:FizBam Foo Bar:52:FizBam Foo Bar:53:FizBam Foo Bar:54:FizBam Foo Bar:55:FizBam Foo Bar:56:FizBam Foo Bar:57:FizBam Foo Bar:58:FizBam Foo Bar:59:FizBam Foo Bar:60:FizBam Foo Bar:61:FizBam Foo Bar:62:FizBam Foo Bar:63:FizBam Foo Bar:64:FizBam Foo Bar:65:FizBam Foo Bar:66:FizBam Foo Bar:67:FizBam Foo Bar:68:FizBam Foo Bar:69:FizBam Foo Bar:70:FizBam Foo Bar:71:FizBam Foo Bar:72:FizBam Foo Bar:73:FizBam Foo Bar:74:FizBam Foo Bar:75:FizBam Foo Bar:76:FizBam Foo Bar:77:FizBam Foo Bar:78:FizBam Foo Bar:79:FizBam Foo Bar:80:FizBam Foo Bar:81:FizBam Foo Bar:82:FizBam Foo Bar:83:FizBam Foo Bar:84:FizBam Foo Bar:85:FizBam Foo Bar:86:FizBam Foo Bar:87:FizBam Foo Bar:88:FizBam Foo Bar:89:FizBam Foo Bar:90:FizBam Foo Bar:91:FizBam Foo Bar:92:FizBam Foo Bar:93:FizBam Foo Bar:94:FizBam Foo Bar:95:FizBam Foo Bar:96:FizBam Foo Bar:97:FizBam Foo Bar:98:FizBam Foo Bar:99:FizBam Foo Bar:100:! 

我然后创建Attribute,我可以使用到一个元素映射到模型属性。

public class MapAttribute : Attribute 
{ 
    public MapAttribute(string fieldKey) 
    { 
     this.Field = fieldKey; 
    } 

    public string Field { get; private set; } 

    public PropertyInfo Property { get; set; } 

    public void SetValue(Question question, string value) 
    { 
     this.Property.SetValue(question, value); 
    } 
} 

现在,在您的模型上,您可以将属性映射到传入字段。当一个字段进来时,你将它传递给该属性以及一个问题的实例并让它分配值。您也可能只是在某种查找表中处理此问题,但属性似乎是允许您向模型添加新属性并更新单个位置中的映射的最简单方法。

我在这里呈现整个模型,其中有100个属性映射到服务器响应。

public class Question 
{ 
    [Map("0")] 
    public string FooBar { get; set; } 

    [Map("1")] 
    public string Id { get; set; } 

    [Map("2")] 
    public string Action { get; set; } 

    [Map("3")] 
    public string Topic { get; set; } 

    [Map("4")] 
    public string Body { get; set; } 

    [Map("5")] 
    public string Time { get; set; } 

    [Map("6")] 
    public string Query { get; set; } 

    [Map("7")] 
    public string Answer { get; set; } 

    [Map("8")] 
    public string __8 { get; set; } 

    [Map("9")] 
    public string __9 { get; set; } 

    [Map("10")] 
    public string __10 { get; set; } 

    [Map("11")] 
    public string __11 { get; set; } 

    [Map("12")] 
    public string __12 { get; set; } 

    [Map("13")] 
    public string __13 { get; set; } 

    [Map("14")] 
    public string __14 { get; set; } 

    [Map("15")] 
    public string __15 { get; set; } 

    [Map("16")] 
    public string __16 { get; set; } 

    [Map("17")] 
    public string __17 { get; set; } 

    [Map("18")] 
    public string __18 { get; set; } 

    [Map("19")] 
    public string __19 { get; set; } 

    [Map("20")] 
    public string __20 { get; set; } 

    [Map("21")] 
    public string __21 { get; set; } 

    [Map("22")] 
    public string __22 { get; set; } 

    [Map("23")] 
    public string __23 { get; set; } 

    [Map("24")] 
    public string __24 { get; set; } 

    [Map("25")] 
    public string __25 { get; set; } 

    [Map("26")] 
    public string __26 { get; set; } 

    [Map("27")] 
    public string __27 { get; set; } 

    [Map("28")] 
    public string __28 { get; set; } 

    [Map("29")] 
    public string __29 { get; set; } 

    [Map("30")] 
    public string __30 { get; set; } 

    [Map("31")] 
    public string __31 { get; set; } 

    [Map("32")] 
    public string __32 { get; set; } 

    [Map("33")] 
    public string __33 { get; set; } 

    [Map("34")] 
    public string __34 { get; set; } 

    [Map("35")] 
    public string __35 { get; set; } 

    [Map("36")] 
    public string __36 { get; set; } 

    [Map("37")] 
    public string __37 { get; set; } 

    [Map("38")] 
    public string __38 { get; set; } 

    [Map("39")] 
    public string __39 { get; set; } 

    [Map("40")] 
    public string __40 { get; set; } 

    [Map("41")] 
    public string __41 { get; set; } 

    [Map("42")] 
    public string __42 { get; set; } 

    [Map("43")] 
    public string __43 { get; set; } 

    [Map("44")] 
    public string __44 { get; set; } 

    [Map("45")] 
    public string __45 { get; set; } 

    [Map("46")] 
    public string __46 { get; set; } 

    [Map("47")] 
    public string __47 { get; set; } 

    [Map("48")] 
    public string __48 { get; set; } 

    [Map("49")] 
    public string __49 { get; set; } 

    [Map("50")] 
    public string __50 { get; set; } 

    [Map("51")] 
    public string __51 { get; set; } 

    [Map("52")] 
    public string __52 { get; set; } 

    [Map("53")] 
    public string __53 { get; set; } 

    [Map("54")] 
    public string __54 { get; set; } 

    [Map("55")] 
    public string __55 { get; set; } 

    [Map("56")] 
    public string __56 { get; set; } 

    [Map("57")] 
    public string __57 { get; set; } 

    [Map("58")] 
    public string __58 { get; set; } 

    [Map("59")] 
    public string __59 { get; set; } 

    [Map("60")] 
    public string __60 { get; set; } 

    [Map("61")] 
    public string __61 { get; set; } 

    [Map("62")] 
    public string __62 { get; set; } 

    [Map("63")] 
    public string __63 { get; set; } 

    [Map("64")] 
    public string __64 { get; set; } 

    [Map("65")] 
    public string __65 { get; set; } 

    [Map("66")] 
    public string __66 { get; set; } 

    [Map("67")] 
    public string __67 { get; set; } 

    [Map("68")] 
    public string __68 { get; set; } 

    [Map("69")] 
    public string __69 { get; set; } 

    [Map("70")] 
    public string __70 { get; set; } 

    [Map("71")] 
    public string __71 { get; set; } 

    [Map("72")] 
    public string __72 { get; set; } 

    [Map("73")] 
    public string __73 { get; set; } 

    [Map("74")] 
    public string __74 { get; set; } 

    [Map("75")] 
    public string __75 { get; set; } 

    [Map("76")] 
    public string __76 { get; set; } 

    [Map("77")] 
    public string __77 { get; set; } 

    [Map("78")] 
    public string __78 { get; set; } 

    [Map("79")] 
    public string __79 { get; set; } 

    [Map("80")] 
    public string __80 { get; set; } 

    [Map("81")] 
    public string __81 { get; set; } 

    [Map("82")] 
    public string __82 { get; set; } 

    [Map("83")] 
    public string __83 { get; set; } 

    [Map("84")] 
    public string __84 { get; set; } 

    [Map("85")] 
    public string __85 { get; set; } 

    [Map("86")] 
    public string __86 { get; set; } 

    [Map("87")] 
    public string __87 { get; set; } 

    [Map("88")] 
    public string __88 { get; set; } 

    [Map("89")] 
    public string __89 { get; set; } 

    [Map("90")] 
    public string __90 { get; set; } 

    [Map("91")] 
    public string __91 { get; set; } 

    [Map("92")] 
    public string __92 { get; set; } 

    [Map("93")] 
    public string __93 { get; set; } 

    [Map("94")] 
    public string __94 { get; set; } 

    [Map("95")] 
    public string __95 { get; set; } 

    [Map("96")] 
    public string __96 { get; set; } 

    [Map("97")] 
    public string __97 { get; set; } 

    [Map("98")] 
    public string __98 { get; set; } 

    [Map("99")] 
    public string __99 { get; set; } 

    [Map("100")] 
    public string __100 { get; set; } 
} 

现在,在建立第一个连接时,获取所有属性及其属性并将它们缓存到字典中。

Dictionary<string, MapAttribute> properties = typeof(Question).GetProperties().ToDictionary(
    property => property.GetCustomAttribute<MapAttribute>().Field, 
    property => 
    { 
     var attribute = property.GetCustomAttribute<MapAttribute>(); 
     attribute.Property = property; 
     return attribute; 
    }); 

在应用程序的生命周期中只做一次。您将在从服务器获得的每个响应中重新使用该字典。现在,当您从服务器收到更新时,只需使用该属性解析它即可。

void Parse(string message, Dictionary<string, MapAttribute> fieldMapping) 
{ 
    string[] messageContent = message.Split(':'); 
    var question = new Question(); 
    for (int index = 0; index < messageContent.Length; index++) 
    { 
     string field = messageContent[index]; 
     MapAttribute mapping = fieldMapping[field]; 
     index++; 
     mapping.SetValue(question, messageContent[index]); 
    } 
} 

这里没有任何真正的花式。我们刚刚使用字典查找和属性来替换switch语句,以便将映射响应键映射到模型属性。大量减少必须编写的最终代码,并让您在将来使用新属性更新模型。简单和容易维护。您可以通过不担心太多性能来达到此目的,直到性能实际上成为可衡量的问题。

+0

来吧,你并不需要粘贴整个“模型”课程,我认为人们会明白这一点。 :)除此之外,如果性能是关键(每秒100条消息不是那么多),你也可以用一个[编译的委托]替换PropertyInfo.SetValue(http://stackoverflow.com/a/17669142/69809 )。 – Groo

+0

@格鲁网站指南是完整的,完整的例子。这是我提供的:)任何人都可以复制/粘贴并运行它,而不必添加所有属性,以验证我的性能数字。 –

+0

尽管如此,遵守的代表无疑是个好主意。 –

0

你可以重构你的代码是这样的:

public void Process(string data) 
{ 
    var assignments = new Dictionary<string, Action<Question, string>>() 
    { 
     { "0", (q, t) => q.Id = t }, 
     { "1", (q, t) => q.Name = t }, 
     // ... 
     { "99", (q, t) => q._99 = t }, 
    }; 

    Question question = new Question(); 
    string[] fields = data.Split(':'); 

    for (int i = 0; i < fields.Length; i += 2) 
    { 
     assignments[fields[i]](question, fields[i + 1]); 
    } 
} 

这消除了开关,并允许您重构在运行时的字典。

我建议,也许这是一个更好的办法:

public class Question 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string Action { get; set; } 
    public string Topic { get; set; } 
    public string Body { get; set; } 
    public string Time { get; set; } 
    public string _99 { get; set; } 

    private static Dictionary<string, Action<Question, string>> __assignments = new Dictionary<string, Action<Question, string>>() 
    { 
     { "0", (q, t) => q.Id = t }, 
     { "1", (q, t) => q.Name = t }, 
     // ... 
     { "99", (q, t) => q._99 = t }, 
    }; 

    public void SetProperty(string key, string value) 
    { 
     __assignments[key](this, value); 
    } 
} 

public void Process(string data) 
{ 
    Question question = new Question(); 
    string[] fields = data.Split(':'); 

    for (int i = 0; i < fields.Length; i += 2) 
    { 
     question.SetProperty(fields[i], fields[i + 1]); 
    } 
} 

而且,作为一个轻微的选择,你可以定义__assignments这种方式,使之干净了一点:

private static Dictionary<string, Action<Question, string>> __assignments = 
     new Action<UserQuery.Question, string>[] 
     { 
      (q, t) => q.Id = t, 
      (q, t) => q.Name = t, 
      // ... 
      (q, t) => q._99 = t, 
     } 
      .Select((x, n) => new { x, n }) 
      .ToDictionary(xn => xn.n.ToString(), xn => xn.x); 
相关问题