如何在没有大型选择/切换的情况下返回特定类类型

时间:2012-01-13 00:49:17

标签: .net protobuf-net

我正在构建一系列类,试图减少主线代码中特定类类型的手动编码。

无论我看哪种方式总是需要手动编写特定类类型。

我希望我能使用反射/激活器等能够使用构造函数中完成的工作来返回classtypes(正确类型),而不需要做大(虽然这里的示例有已减少)选择/切换GetPacket中的语句。

我知道这是VB.Net,但该项目已经用该语言编写,我不介意如果你发布C#示例我只会转换它们。 但请不要将其作为VB.Net问题重新讨论,因为它不是关于如何在框架内完成它的语言。

Imports ProtoBuf

Public Class CompatiblePackets
    Inherits Dictionary(Of Packet.PacketType, Base)

    Public Sub New()
        Dim theAssembly As Assembly = Assembly.GetExecutingAssembly
        For Each t As Type In theAssembly.GetTypes
            If t.BaseType Is GetType(Base) Then
                Dim p As Base = CType(t.Assembly.CreateInstance(t.FullName), Base)
                Me.Add(p.PacketTypeIndicator, p)
            End If
        Next
    End Sub

    Public Function GetPacket(id As PacketType, data As Stream) As Base
        Dim activePacket As Base
        If Me.ContainsKey(id) Then
            activePacket = Me(id)
        Else
            activePacket = Me(PacketType.Generic)
        End If
        Try
            Select Case id
                Case PacketType.AcknowledgeBulk
                    Return GetPacket(Of AcknowledgeBulk)(activePacket, data)
                Case PacketType.Generic
                    Return GetPacket(Of Generic)(activePacket, data)
                Case PacketType.Identification
                    Return GetPacket(Of Identification)(activePacket, data)

    '''There are so far about 20 more packet types in the real code.

                Case Else
                    'unknown type "Computer says No!"
            End Select
        Catch ex As Exception
            If data.GetType Is GetType(MemoryStream) Then
                Debug.Print(Core.Text.OutputData(CType(data, MemoryStream).ToArray))
            End If
            Throw
        End Try
        Debug.Print("Wtf - " & id.ToString())

        Return New NoOperation
    End Function

    Private Function GetPacket(Of t)(activePacket As Packet.Base, data As Stream) As t
        Return Serializer.Deserialize(Of t)(data)
    End Function
End Class

2 个答案:

答案 0 :(得分:2)

如果我正确理解了这个问题,看起来你应该使用Serializer.NonGeneric;这有多种方法可以手动传递Type而不使用泛型,包括SerializeDeserialize

请注意,对于此方案,Serializer.NonGeneric还有针对异构标头标记邮件的特定API,您可能会发现这对于网络流方案非常有用:

static void Main() {
    // memory-stream only for example - would work fine with NetworkStream too
    using(var ms = new MemoryStream()) {
        // this is just to simulate an incoming network-stream
        Serializer.SerializeWithLengthPrefix(ms, new Foo { Name = "abc" },
           PrefixStyle.Base128, 1); // tag 1 for Foo
        Serializer.SerializeWithLengthPrefix(ms, new Bar { Value = 123 },
           PrefixStyle.Base128, 2); // tag 2 for Bar
        ms.Position = 0;

        // now we'll consume it

        //first setup some map of 1=Foo, 2=Bar - any mechanism will suffice
        var typeMap = new Dictionary<int, Type>{
            {1, typeof(Foo)},
            {2, typeof(Bar)}
        };
        Serializer.TypeResolver resolver = i => {
            Type type;
            return typeMap.TryGetValue(i, out type) ? type : null;
        };
        object obj;
        while(Serializer.NonGeneric.TryDeserializeWithLengthPrefix(
            ms, PrefixStyle.Base128,resolver,out obj))
        {
            Console.WriteLine(obj);
        }
        Console.WriteLine("<EOF>");

    }
}
[ProtoContract]
class Foo {
    [ProtoMember(7)]
    public string Name{ get; set; }

    public override string ToString() { return "Foo: " + Name; }
}
[ProtoContract]
class Bar {
    [ProtoMember(4)]
    public int Value { get; set; }

    public override string ToString() { return "Bar: " + Value; }
}

输出:

Foo: abc
Bar: 123
<EOF>

在v2中,请注意TypeModel将非通用API公开为主要接口(Serializer API仅作为默认模型的代理)。

答案 1 :(得分:0)

我没有对此进行过测试,但我认为你唯一的选择是反思善良的令人愉快的大杂烩:

Private Function GetPacket(oPacketType As PacketType, data As Stream) As Base

    Dim oDeserializeMethod As System.Reflection.MethodInfo
    Dim oGenericMethod As System.Reflection.MethodInfo
    Dim oType As Type

    ' Fetch the System.Type for the current packet type. 
    ' This assumes that the enum and the class names are the same
    oType = System.Reflection.Assembly.GetExecutingAssembly.GetType(oPacketType.ToString)

    ' Fetch the deserialize method from Protobuf Serializer
    oDeserializeMethod = GetType(Serializer).GetMethod("Deserialize")

    ' Create a generic method from the deserialize method
    oGenericMethod = oDeserializeMethod.MakeGenericMethod(oType)

    ' And finally, invoke the method
    Return TryCast(oGenericMethod.Invoke(Serializer, Data), Base)
End Function

然后你可以在不需要案例陈述的情况下调用它:

Return GetPacket(id, data)