【问题标题】:Is it possible to optimize large switch statements in C#?是否可以在 C# 中优化大型 switch 语句?
【发布时间】:2021-08-11 19:08:57
【问题描述】:

我正在开发一个 websocket 客户端应用程序。服务器以 JSON 格式发送消息,我想反序列化它。 JSON格式数据中有一个字符串表示消息的类型(今天大约有50种,以后可能会更多)。

所以我写了一个像这样的大型 switch 语句:

switch(type){
    case "type1":
        DoSth<T1>(DeserializeFunction<T1>(message));
        break;
    case "type2":
        DoSth<T2>(DeserializeFunction<T2>(message));
        break;
    //...
}

这个语句可以优化吗?

这是模型:

public record EventMessage<T> where T : IEventExtraBody
    {
        // this will always be 0
        [JsonPropertyName("s")]
        public int EventType { get; set; }
        
        [JsonPropertyName("sn")]
        public long SerialNumber { get; set; }
        
        [JsonPropertyName("d")]
        public EventMessageData<T> Data { get; set; }

        public override string ToString()
        {
            return JsonSerializer.Serialize(this);
        }
    }

public record EventMessageData<T> where T : IEventExtraBody
    {
        // Some other properties        

        [JsonPropertyName("extra")]
        public EventMessageExtra<T> Extra { get; set; }
    }

public record EventMessageExtra<T> where T : IEventExtraBody
    {
        [JsonPropertyName("type")]
        public string Type { get; set; } // this string indicates the type of message
        
        [JsonPropertyName("body")]
        public T Body { get; set; }
    }

正文(示例):

public record ExitedGuildEvent : IEventExtraBody
    {
        [JsonPropertyName("user_id")]
        public string UserId { get; set; }

        [JsonPropertyName("exited_at")]
        public long ExitedAt { get; set; }
    }

当消息到达时,我使用JsonDocument 来获取类型字符串。

var typeString = JsonDocument.Parse(message.Text).RootElement.GetProperty("d").GetProperty("extra").GetProperty("type").GetString()

然后,我想反序列化消息并发布到MessageHub

反序列化json字符串并发布:

_messageHub.Publish(JsonSerializer.Deserialize<EventMessage<BodyType>>(message.Text));

而且因为BodyType很多,而EventMessage&lt;Type.GetType("TypeClassPath")&gt;(message.Text)是非法的,所以我写了一个大的switch语句。

也许我为这种情况建立了一个非常糟糕的模型。希望大家多多指教。

【问题讨论】:

标签: c# .net optimization


【解决方案1】:

您可以将switch-case 替换为hashmap。为此,您只需将每个案例移动到单独的函数中。在这里您可以创建一个工厂方法来帮助您填写哈希图,因为情况非常相似

public class YourHub
{
    private IMessageHub _messageHub = new MessageHub();
    private Dictionary<string, Action<string, IMessageHub>> _methods;

    public YourHub()
    {
        //fill out the hashmap for all types that you have
        //make sure this hashmap is shared between operations
        _methods = new Dictionary<string, Action<string, IMessageHub>>()
        {
            {"key1",  CreateAction<EventMessage<ExitedGuildEvent>>() }
        };
    }

    //factory method for the actions
    private Action<string, IMessageHub> CreateAction<T>()
    {
        return (json, hub) => hub.Publish(JsonSerializer.Deserialize<T>(json, null));
    }

    public void ProcessMessage(string json)
    {
        var typeString = JsonDocument
             .Parse(json)
             .RootElement.GetProperty("d")
             .GetProperty("extra")
             .GetProperty("type")
             .GetString();
                    
        if (!_methods.ContainsKey(typeString)) throw new NotSupportedException();            
        var method = _methods[typeString]; 

        method(json, _messageHub);
    }      
}

这种方法不会在 50 个元素上给您带来巨大的性能提升,但它看起来更简洁。运行时复杂度是 O(1)O(n)switch-case 相比,但它需要 O(n) 额外的空间。

【讨论】:

    【解决方案2】:

    比大开关更好的解决方案可能是将 DeserializeFunction 重构为接口和类。

    按类型注册,然后解析。使用 DI 容器或映射到的字典。

    interface IMessageDeserializer {
    
        object Deserialize(Message message);
    
    }
    
    class Type1Deserializer : IMessageDeserializer {
    
        public object Deserialize(Message message){
          // Implementation that returns a Type1
          return new Type1(){
    
          };
        }
    
    }
    
    
    // Register your serializers (you can use also a DI container  but this is simpler just to show how) in a dictionary, preferably reused
    
    Dictionary<Type, IMessageDeserializer> serializers = new Dictionary<Type, IMessageDeserializer>();
    
    serializers.Add("type1", new Type1Deserializer());
    serializers.Add("type2", new Type2Deserializer());
    serializers.Add("type3", new Type3Deserializer());
    
    // When you need it, use it like this:
    
    string type = "type1"; // This is from your other code
    var message = GetMessage(); // This is from your other code
    
    IMessageDeserializer serializer = serializers[type];    
    object deserializedMessage = serializer.Deserialize(message);
    
    // To create your event message, either add a constraint to the T of IMessageDeserializer so you can pass it into another function that creates the event message or just simply return the messagehub message as json directly from your IMessageDeserializer implementation)
    

    (我是凭记忆写的,如有错误,我深表歉意)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 2015-06-09
      • 1970-01-01
      • 2021-11-04
      相关资源
      最近更新 更多