How to return specific class types without a lot of selection / switching

I am building a series of classes, trying to reduce the manual coding of certain types of classes from the main code.

Whatever way I look, there is always somewhere where manual coding of a particular type of class is required.

I was hoping that I could use the reflection / activator function, etc., to be able to use the work done in the constructor, to be able to return class types (of the desired type) without having to do large ones (although the example here has been shortened) select / switch statement that is in GetPacket .

I know that this is VB.Net, but the project has already been written in this language, I do not mind if you publish examples in C #, I just convert them. But please do not repeat the VB.Net question because it is not about how to do this within the framework.

 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 
+4
source share
2 answers

If I understand the question correctly, it looks like you should use Serializer.NonGeneric ; it has various methods for passing Type manually and without using generics, including Serialize and Deserialize .

Note that for this scenario, Serializer.NonGeneric also has a specific API for heterogeneous messages with a header, which may be useful for a network stream script:

 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; } } 

It is output:

 Foo: abc Bar: 123 <EOF> 

In v2, note that TypeModel does not provide a common API as the main interface (the Serializer API simply acts as a proxy for the default model).

+2
source

I have not tested this, but I think your only option is a delightful hash of reflection:

 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 

Then you could call it without the need for a case statement:

 Return GetPacket(id, data) 
0
source

Source: https://habr.com/ru/post/1390748/


All Articles