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

﹥>﹥吖頭↗ 提交于 2019-12-01 07:28:26

问题


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 currently do something like

for ( i=0;i<tmpArrayList.Count;i++)
{
   object x=tmpArrayList[i];
   if (x.GetType() ==  typeof(byte))
   {
      wrt.Write((byte)x);
   }
   ........

The problem is that if miss a type them my code might break in the future.

I would like to do something like.

object x=tmpArrayList[i];
wrt.Write(x);

but it doesn't work unless I do each cast.

Edit:

After consulting the answers this is what I came up with for the function. For testing this function sends the array to syslog.

  private void TxMsg(ArrayList TxArray,IPAddress ipaddress)
  {
     Byte[] txbuf=new Byte[0];
     int sz=0;

     // caculate size of txbuf
     foreach (Object o in TxArray)
     {
        if ( o is String ) 
        {
           sz+=((String)(o)).Length;
        }
        else if ( o is Byte[] )
        {
           sz+=((Byte[])(o)).Length;
        }
        else if ( o is Char[] )
        {
           sz+=((Char[])(o)).Length;
        }
        else // take care of non arrays
        {
           sz+=Marshal.SizeOf(o);
        }
     }
     txbuf = new Byte[sz];

     System.IO.MemoryStream stm_w = new System.IO.MemoryStream( txbuf, 0,txbuf.Length);
     System.IO.BinaryWriter wrt = new System.IO.BinaryWriter( stm_w );

     foreach (Object o in TxArray)
     {
        bool otypefound=false;
        if (o is String) // strings need to be sent one byte per char
        {
           otypefound=true;
           String st=(String)o;
           for(int i=0;i<st.Length;i++)
           {
              wrt.Write((byte)st[i]);
           }
        }
        else
        {
           foreach (MethodInfo mi in typeof(BinaryWriter).GetMethods())
           {
              if (mi.Name == "Write")
              {
                 ParameterInfo[] pi = mi.GetParameters();
                 if ((pi.Length == 1)&&(pi[0].ParameterType==o.GetType()))
                 {
                    otypefound=true;
                    mi.Invoke(wrt, new Object[] { o });
                 }
              }
           }
        }
        if(otypefound==false)
        {
           throw new InvalidOperationException("Cannot write data of type " + o.GetType().FullName);
        }
     }
     IPEndPoint endpoint = new IPEndPoint(ipaddress, 514); //syslog port
     UdpClient udpClient_txmsg = new UdpClient();
     udpClient_txmsg.Send(txbuf, txbuf.Length,endpoint); // send udp packet to syslog             
  }

回答1:


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



回答2:


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.




回答3:


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?




回答4:


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



回答5:


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.




回答6:


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.



来源:https://stackoverflow.com/questions/360111/is-there-a-way-cast-an-object-back-to-it-original-type-without-specifing-every-c

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