有没有一种方法可以将对象强制转换回其原始类型而不指定每种情况?

发布于 2024-07-10 08:12:54 字数 2440 浏览 9 评论 0原文

我有一个不同类型对象的数组,我使用 BinaryWriter 将每个项目转换为其二进制等效项,以便我可以通过网络发送该结构。

我目前正在做类似的事情,

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

问题是,如果错过了它们的类型,我的代码将来可能会崩溃。

我想做类似的事情。

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

但除非我完成每个演员表,否则它不起作用。

编辑:

在咨询了答案之后,这就是我对该功能的想法。 为了测试此函数将数组发送到系统日志。

  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             
  }

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             
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

南街九尾狐 2024-07-17 08:12:54

不可以。必须在编译时知道强制转换,但实际类型只有在执行时才知道。

但请注意,有一种更好的方法可以调用 GetType 来测试类型。 而不是:

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

使用:

if (x is byte)

编辑:回答额外的问题:

“所有类型是什么?” 好吧,看看 BinaryWriter 的文档,我想......

“我需要担心字节和字节吗?” 不,byte 是 C# 中 System.Byte 的别名。 他们是同一类型。

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.

倾城花音 2024-07-17 08:12:54

这是使用反射的 BinaryWriter 解决方案。

这基本上会扫描 BinaryWriter 中名为 Write 的方法,该方法只接受一个参数,然后构建一个字典,其中的方法处理哪种类型,然后对于每个要写入的对象,找到正确的方法并在编写器上调用它。

肮脏,你可能应该寻找更好的方法来完成整个事情(而不仅仅是写作部分),但它应该适合你当前的需求:

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

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);
                }
            }
        }
    }
}
绿光 2024-07-17 08:12:54

乔恩是对的,但我还有另一个想法,你可能会觉得有用。 您是否考虑过在每个对象的传输中添加另一个字节,然后使用该字节作为类型代码,告诉您在另一端将其转换为什么?

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?

不喜欢何必死缠烂打 2024-07-17 08:12:54

您是否考虑过使用 BinaryFormatter 而不是 BinaryWriter?

优点

  • 您可以传递对象(即任何东西),因此它解决了您的转换问题。
  • 自动类型管理(实际上将类型标头写入流)。
  • 还支持复杂的引用类型。

缺点

在内部使用序列化,因此:

  • 可能较慢。
  • 字节流变得更大(因为类型标头)。
  • 您无法控制字节格式,因此在互操作场景中不是一个选项。
  • 潜在的版本问题(序列化类型的不同程序集版本之间的兼容性)。
  • 需要序列化代码访问权限(与部分信任场景相关)。

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).
九八野马 2024-07-17 08:12:54

您要求的是动态调度,而C# 3.0没有它。

您至少应该使用运行时检查来验证您没有丢失类型。

如果您有一个从类型映射到处理函数的字典,您也许可以做一些聪明的事情。 您可以在一处填写所有处理功能的映射。 与在处理发生的任何地方编写开关相比,您更有可能做到这一点。

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.

浮世清欢 2024-07-17 08:12:54

这是需要一种名为 Double Dispatch 的情况。

我假设 wrt 对象是您自己编写的对象(假设它是 Writer 类型)。 您可以执行以下操作:

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

Writer 所做的就是分派回输入,告诉它使用它(Writer)进行写入,但是这是针对该对象完成的,无法提前知道。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文