Is there a way cast an object back to it original type without specifing every case?

前端 未结 6 1374
眼角桃花
眼角桃花 2021-01-15 14:28

I have an array of different type objects and I use a BinaryWriter to convert each item to its binary equivalent so I can send the structure over the network.

I curr

相关标签:
6条回答
  • 2021-01-15 15:07

    What you're asking for is Dynamic Dispatch, and C# 3.0 doesn't have it.

    You should at least use a runtime check to verify that you aren't missing a type.

    You may be able to do something clever where you have a Dictionary that maps from types to processing functions. You can fill in the mapping for all processing functions in one place. You have a better chance of getting this right than if you write a switch wherever the processing happens.

    0 讨论(0)
  • 2021-01-15 15:08

    This is a case of needing something called Double Dispatch.

    I'm going to assume that the wrt object is one you wrote yourself (Let's say it is of type Writer). Here is what you could do:

    class Writer
    {
        void write(byte b)
        {
            // write bytes here
        }
    
        void write(Writable something)
        {
            something.writeOn(this);
        }
    }
    
    interface Writeable
    {
        void writeOn(Writer writer);
    }
    
    class SomeObject implements Writeable
    {
        private Object someData;
        private Object moreData;
    
        void writeOn(Writer writer)
        {
            writer.write(convertToByte(someData));
            writer.write(convertToByte(moreData));
        }
    }
    
    class AnotherObject implements Writeable
    {
        private int x;
        private int y;
        private int z;
    
        void writeOn(Writer writer)
        {
            writer.write((byte)x);
            writer.write((byte)y);
            writer.write((byte)z);
        }
    }
    

    What Writer is doing is dispatching back to the input, telling it to use it (the Writer) to write, however that is done for that object, which cannot be known ahead of time.

    0 讨论(0)
  • 2021-01-15 15:12

    Jon's right, but I had another thought that you may find useful. Have you considered adding in another byte to the transmission of each object, then using that byte as a type code, telling you what to cast it to on the other end?

    0 讨论(0)
  • 2021-01-15 15:19

    No. The cast has to be known at compile-time, but the actual type is only known at execution time.

    Note, however, that there's a better way of testing the type calling GetType. Instead of:

    if (x.GetType() == typeof(byte))
    

    Use:

    if (x is byte)
    

    EDIT: To answer the extra questions:

    "What are all the types?" Well, look down the docs for BinaryWriter, I guess...

    "Do I need to worry about byte and Byte?" No, byte is an alias for System.Byte in C#. They're the same type.

    0 讨论(0)
  • 2021-01-15 15:19

    Have you considered using a BinaryFormatter instead of the BinaryWriter?

    Advantages

    • You can pass objects (i.e. anything), so it solves your casting problem.
    • Automatic type management (actually writes type headers to the stream).
    • Supports complex reference types as well.

    Disadvantages

    Uses Serialization internally, therefore:

    • Probably slower.
    • Byte stream gets larger (because of the type headers).
    • You don't have control over the byte format, therefore not an option in interop scenarios.
    • Potential version issues (compatibility between different assembly versions of the serialized type).
    • Requires the serialization code access permission (relevant in partial trust scenarios).
    0 讨论(0)
  • 2021-01-15 15:23

    Here is a solution for BinaryWriter that uses reflection.

    This basically scans BinaryWriter for methods named Write that takes exactly one parameter, then builds a dictionary of which method handles which type, then for each object to write, finds the right method and calls it on the writer.

    Dirty, and you should probably look for better ways of doing the whole thing (not just the writing part), but it should work for your current needs:

    using System.IO;
    using System;
    using System.Reflection;
    using System.Collections.Generic;
    namespace ConsoleApplication14
    {
        public class Program
        {
            public static void Main()
            {
                Dictionary<Type, MethodInfo> mapping = new Dictionary<Type, MethodInfo>();
                foreach (MethodInfo mi in typeof(BinaryWriter).GetMethods())
                {
                    if (mi.Name == "Write")
                    {
                        ParameterInfo[] pi = mi.GetParameters();
                        if (pi.Length == 1)
                            mapping[pi[0].ParameterType] = mi;
                    }
                }
    
                List<Object> someData = new List<Object>();
                someData.Add((Byte)10);
                someData.Add((Int32)10);
                someData.Add((Double)10);
                someData.Add((Char)10);
                someData.Add("Test");
    
                using (FileStream file = new FileStream(@"C:\test.dat", FileMode.Create, FileAccess.ReadWrite))
                using (BinaryWriter writer = new BinaryWriter(file))
                {
                    foreach (Object o in someData)
                    {
                        MethodInfo mi;
                        if (mapping.TryGetValue(o.GetType(), out mi))
                        {
                            mi.Invoke(writer, new Object[] { o });
                        }
                        else
                            throw new InvalidOperationException("Cannot write data of type " + o.GetType().FullName);
                    }
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题