2016-09-27 63 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); 
相關問題