2

さまざまなタイプのオブジェクトの配列があり、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);

でも、キャストごとにしないとうまくいきません。

編集:

答えを調べた後、これが関数について思いついたものです。テストのために、この関数は配列を 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             
  }
4

6 に答える 6

7

いいえ。キャストはコンパイル時に認識される必要がありますが、実際の型は実行時にのみ認識されます。

ただし、GetType を呼び出す型をテストするより良い方法があることに注意してください。それ以外の:

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

使用する:

if (x is byte)

編集:追加の質問に答えるには:

「全部で何型?」さて、BinaryWriter のドキュメントを調べてみてください...

「バイトとバイトは気にしなくていいの?」いいえ、byte は C# の System.Byte のエイリアスです。彼らは同じタイプです。

于 2008-12-11T17:05:16.857 に答える
3

ジョンの言うとおりですが、役に立つかもしれない別の考えがありました。各オブジェクトの送信に別のバイトを追加し、そのバイトを型コードとして使用して、反対側で何にキャストするかを伝えることを検討しましたか?

于 2008-12-11T17:09:30.733 に答える
3

BinaryWriterの代わりにBinaryFormatterを使用することを検討しましたか?

利点

  • object s (つまり何でも)を渡すことができるので、キャストの問題が解決されます。
  • 自動型管理 (実際に型ヘッダーをストリームに書き込みます)。
  • 複雑な参照型もサポートします。

短所

内部でシリアル化を使用するため、次のようになります。

  • おそらく遅いです。
  • バイト ストリームが大きくなります (タイプ ヘッダーのため)。
  • バイト形式を制御できないため、相互運用シナリオのオプションではありません。
  • 潜在的なバージョンの問題 (シリアル化された型の異なるアセンブリ バージョン間の互換性)。
  • シリアル化コード アクセス許可が必要です (部分信頼シナリオに関連)。
于 2008-12-11T17:41:26.817 に答える
3

リフレクションを使用する BinaryWriter のソリューションを次に示します。

これは基本的に BinaryWriter をスキャンして、正確に 1 つのパラメーターを受け取る 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);
                }
            }
        }
    }
}
于 2008-12-11T18:07:15.140 に答える
2

あなたが求めているのはDynamic Dispatchで、C# 3.0 にはありません。

少なくとも実行時チェックを使用して、タイプが欠落していないことを確認する必要があります。

Dictionary型から処理関数にマップする があれば、何か賢いことができるかもしれません。すべての処理関数のマッピングを 1 か所で入力できます。処理が行われる場所にスイッチを記述するよりも、これを正しく行う可能性が高くなります。

于 2008-12-11T17:15:33.107 に答える
1

これは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) を使用して書き込むように指示することですが、それはそのオブジェクトに対して行われるため、事前に知ることはできません。

于 2008-12-11T17:29:38.180 に答える