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

百般思念 提交于 2019-12-01 11:35:12

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

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.

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?

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).

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!