Search code examples
c#castingsyslogudpclient

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


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             
  }

Solution

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