C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: 構造体の使い方


(過去ログ 113 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■67054 / inTopicNo.1)  構造体の使い方
  
□投稿者/ 裕猫 (57回)-(2013/06/27(Thu) 16:39:27)

分類:[C#] 

開発環境
OS: Windows7
言語: VisualStudio2008 C#

複数のプロジェクトで同じ構造体に同じデータを渡すので、構造体にデータを渡す部分をクラスライブラリにしたいのです。

クラスライブラリ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DBS
{
    public class Kouzoutai
    {
        public struct NOUGYOU
        {
            public string 米;
            public string 産地;
            public string 生産日;
            public int 金額;
        }

        public struct KOUGYOU
        {
            public string 部品;
            public string 生産日;
            public int 金額;
        }
    }

    public class KCopy
    {
        public static void Kangen(object[] DT, struct KOUZOU)
        {
            switch (DT)
            {
                case "Sisuoka":
			kouzou.米 = DT[0];
			kouzou.産地 = DT[1];
			kouzou.生産日 = DT[2];
			kouzou.金額 = DT[3];
			break;
                case "Fuji":
			kouzou.部品 = DT[0];
			kouzou.生産日 = DT[1];
			kouzou.金額 = DT[2];
			break;
                default:
                    	break;
            }
        }
    }
}

アプリ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBS;

namespace 構造体TEST
{
    class Program
    {
        object[] Sisuoka = new object[4]
        object[] Fuji = new object[3]

        static void Main(string[] args)
        {
            NOUGYOU 例 = new NOUGYOU();
            KOUGYOU 金属 = new KOUGYOU();
            KCopy.Kangen(Sisuoka, 例);
            KCopy.Kangen(Fuji, 金属);

            Console.WriteLine(例.米);
            Console.WriteLine(例.産地);
            Console.WriteLine(例.生産日);
            Console.WriteLine(例.金額.ToString());

            Console.WriteLine(金属.部品);
            Console.WriteLine(金属.生産日);
            Console.WriteLine(金属.金額.ToString());
            Console.ReadLine();
        }
    }
}

上記サンプルのように構造体の設定とデータの設定をクラスライブラリにしておき、アプリよりKCopy.KangenでSisuokaやFujiで持っているデータを構造体に

コピーしたいのですがpublic static void Kangen(object[] DT, struct KOUZOU)でstruct KOUZOUは使えないとエラーを出します。どうやって渡したらいいの

でしょうか?教えてください。よろしくお願いいたします。

引用返信 編集キー/
■67055 / inTopicNo.2)  Re[1]: 構造体の使い方
□投稿者/ ABR (1回)-(2013/06/27(Thu) 17:02:23)
> コピーしたいのですがpublic static void Kangen(object[] DT, struct KOUZOU)でstruct KOUZOUは使えないとエラーを出します。どうやって渡したらいいの
>
> でしょうか?教えてください。よろしくお願いいたします。

KOUZOUって何の名前か分かりませんが、structって型の名前じゃないから、これではダメなのではないでしょうか。
それよりも、各構造体でコピーのメソッドを用意した方がよっぽどいいと思います。
引用返信 編集キー/
■67058 / inTopicNo.3)  Re[1]: 構造体の使い方
□投稿者/ 魔界の仮面弁士 (251回)-(2013/06/27(Thu) 21:51:43)
No67054 (裕猫 さん) に返信
> object[] Sisuoka = new object[4]
セミコロンが抜けているのはともかくとして、
変数の中身が空っぽですが、それで良いのでしょうか?


> NOUGYOU 例 = new NOUGYOU();
これだと名前空間が通らないハズです。代入式の右辺は

 × new NOUGYOU();
 〇 new Kouzoutai.NOUGYOU();
 〇 new DBS.Kouzoutai.NOUGYOU();
 〇 new global::DBS.Kouzoutai.NOUGYOU();

ではないでしょうか。変数宣言の型名も然り。


>   KCopy.Kangen(Sisuoka, 例);
ここは静的メソッド(static void Main) の中ですから、
インスタンス変数 Sisuoka にはアクセスできません。

インスタンスメソッドからアクセスするようにするか、もしくは
object[] な変数を静的にしてみてください。


> struct KOUZOUは使えないとエラーを出します。
元のコードが文法的に問題だらけなので、やりたいことに合致しているか
自信がありませんが、これで如何でしょう。


//----------------------
namespace DBS
{
    public class Kouzoutai
    {
        public struct NOUGYOU
        {
            public string 米;
            public string 産地;
            public string 生産日;
            public int 金額;
        }

        public struct KOUGYOU
        {
            public string 部品;
            public string 生産日;
            public int 金額;
        }
    }
}
//----------------------
using System;
using DBS;

namespace 構造体TEST
{
    class Program
    {
        static object[] Sisuoka = {"コシヒカリ", "福島", "2012/09/25", 3000};
        static object[] Fuji = { "平座金M5/SUS304", "2012/01/25", 3500 };

        static void Main(string[] args)
        {
            Kouzoutai.NOUGYOU 例 = new Kouzoutai.NOUGYOU();
            Kouzoutai.KOUGYOU 金属 = new Kouzoutai.KOUGYOU();

            Sample.FillValues(ref 例, Sisuoka);
            Sample.FillValues(ref 金属, Fuji);

            Console.WriteLine(例.米);
            Console.WriteLine(例.産地);
            Console.WriteLine(例.生産日);
            Console.WriteLine(例.金額.ToString());
            Console.WriteLine("------------");
            Console.WriteLine(金属.部品);
            Console.WriteLine(金属.生産日);
            Console.WriteLine(金属.金額.ToString());
            Console.WriteLine("------------");

            例 = Sample.ToStruct<Kouzoutai.NOUGYOU>("日本晴", "新潟", "2012/09/22", 3000);
            Console.WriteLine(例.米);
            Console.WriteLine(例.産地);
            Console.WriteLine(例.生産日);
            Console.WriteLine(例.金額.ToString());

            Console.ReadLine();
        }
    }
}

//----------------------
internal static class Sample
{
    public static T ToStruct<T>(params object[] values) where T : struct
    {
        T result = default(T);
        FillValues(ref result, values);
        return result;
    }

    public static void FillValues<T>(ref T t, params object[] values) where T : struct
    {
        var fields = typeof(T).GetFields().ToArray();
        int limit = Math.Min(fields.Length, values.Length);
        object box = t;
        for (int i = 0; i < limit; i++) fields[i].SetValue(box, values[i]);
        t = (T)box;
    }
}
//----------------------

引用返信 編集キー/
■67063 / inTopicNo.4)  Re[2]: 構造体の使い方
□投稿者/ 裕猫 (58回)-(2013/06/28(Fri) 09:32:10)
No67058 (魔界の仮面弁士 さん) に返信
> セミコロンが抜けているのはともかくとして、
> 変数の中身が空っぽですが、それで良いのでしょうか?
おはようございます。すみませんセミコロン付け忘れていました。変数の中身はデータベースから読み込んだバイトデータが入ります。説明忘れました。
>>NOUGYOU 例 = new NOUGYOU();
> これだと名前空間が通らないハズです。代入式の右辺は
>
>  × new NOUGYOU();
>  〇 new Kouzoutai.NOUGYOU();
>  〇 new DBS.Kouzoutai.NOUGYOU();
>  〇 new global::DBS.Kouzoutai.NOUGYOU();
>
> ではないでしょうか。変数宣言の型名も然り。
クラス名つけるのを忘れました。すみません。

やりたいことはデータベースA1から読み込んだバイトデータ Sisuoka は必ずNOUGYOUという構造体にstring int に変換していれて使います。これはどのアプリからでも必ずそうするので
アプリごとにアプリ内でデータ変換して入れると何回も同じ事を書かなければなりません。この方法なら問題なくできるのですが、何回も同じ事を書かなければならないので、クラスライブラリにNOUGYOUにSisuokaをデータ変換していれる作業を入れて、アプリ本体ではKCopy.Kangen(データのオブジェクト, データを受け取る構造体名);とすればいいだけにしたいのです。
(プログラムの中には)
ABRさんのKOUZOUがなんの名前かわからないとの答えになりますがクラスライブラリのKCopy.Kangenで使うデータを受け取る構造体名であり、適当につけた名前です。
データベースの数と構造体のパターンは100種類あるので汎用になるようにデータベースのオブジェクト名で構造体のパターンを決めて処理するように、switch文で100パターン書いておいて各アプリからKCopy.Kangen(データのオブジェクト, データを受け取る構造体名);で構造体にデータをセットしたいのです。
 アプリの中で宣言したKouzoutai.NOUGYOU 例 = new Kouzoutai.NOUGYOU(); の例はどうやってクラスライブラリのKCopy.Kangenで受け取れるようになるでしょう?
いろいろ調べているのですが回答にたどり着けません。よろしくお願いいたします。
引用返信 編集キー/
■67064 / inTopicNo.5)  Re[3]: 構造体の使い方
□投稿者/ 魔界の仮面弁士 (252回)-(2013/06/28(Fri) 11:33:37)
2013/06/28(Fri) 13:50:20 編集(投稿者)

No67063 (裕猫 さん) に返信
> 汎用になるようにデータベースのオブジェクト名で構造体のパターンを決めて処理するように、
ひとつのメソッドの中で 100 パターンの switch を実装するのではなく、
構造体の数だけオーバーロードを実装した方が安全かと思います。

あるいは ABR さんが書かれたように、個々の構造体側にメソッドを
装備するとか、各メンバーにカスタム属性を付与して、object[] と
データ交換できるような仕組みを作ってみるとか。

>>> void Kangen(object[] DT, struct KOUZOU)
>> KOUZOUって何の名前か分かりませんが、structって型の名前じゃないから、これではダメなのではないでしょうか。
> KOUZOUがなんの名前かわからないとの答えになりますがクラスライブラリのKCopy.Kangenで使うデータを受け取る構造体名であり、適当につけた名前です。
ABR さんが注視しておられるのは、KOUZOU についてではなく、
struct の方だと思いますよ。

上記の場合、引数定義に「struct KOUZOU」と書いてありますが、これだと
この引数は「struct 型の KOUZOU という名前の変数」となります。

しかし、struct というのは C# にとっては予約語であり、
型の名前としては使えません。たとえば、下記はエラーになります。
(どうしても予約語を使いたければ、@ 付きで宣言する必要アリ)

 struct struct { int a; }
 void Kangen(object[] DT, struct KOUZOU) { }


> どうやってクラスライブラリのKCopy.Kangenで受け取れるようになるでしょう?
構造体を表す総称型として引数を用意したいのであれば、
こういった案が考えられます。


(案1) object 型で受け取って、内部でキャストして使う
 void Kangen(object[] DT, object structure) { }

(案2) ValueType 型で受け取って、内部でキャストして使う
 void Kangen(object[] DT, ValueType structure) { }

(案3) 構造体の個数分だけオーバーロードを用意する
 void Kangen(object[] DT, Kouzoutai.KOUGYOU structure) { }
 void Kangen(object[] DT, Kouzoutai.NOUGYOU structure) { }

(案4) struct 制限をつけたジェネリックメソッドとする
 void Kangen<T>(object[] DT) where T : struct { }


先の私のコードは、案4 に リフレクションを加えた物ですね。
引用返信 編集キー/
■67067 / inTopicNo.6)  Re[4]: 構造体の使い方
□投稿者/ 裕猫 (59回)-(2013/06/28(Fri) 13:45:08)
No67064 (魔界の仮面弁士 さん) に返信
>>汎用になるようにデータベースのオブジェクト名で構造体のパターンを決めて処理するように、
> 中で switch するぐらいなら、メソッド側をオーバーロードした方が手っ取り早いかと。
> あるいは ABR さんが書かれたように、個々の構造体側にメソッドを装備するとか。
オーバーロードも考えて見ます。
>>どうやってクラスライブラリのKCopy.Kangenで受け取れるようになるでしょう?
> オーバーロードせずに単一のメソッドにしておきたいのであれば、先に投稿のように、
> ジェネリックで struct な型を受け取るのでは駄目でしょうか。
ジェネリックは知らなかったので理解しておりません。調べて使ってやってみます。ありがとうございます。
引用返信 編集キー/
■67068 / inTopicNo.7)  Re[5]: 構造体の使い方
□投稿者/ 魔界の仮面弁士 (253回)-(2013/06/28(Fri) 14:22:28)
No67067 (裕猫 さん) に返信
> ■No67064 (魔界の仮面弁士 さん) に返信

済みません。分かりにくいかと思って、入れ違いで
回答をゴッソリ書き換えてしまいました…。

多量の switch コードは、さすがに問題があると思います。

型安全にするならオーバーロードで実装するのが確実かと思いますが、
100個もオーバーロードするようなライブラリというのもどうなのでしょうね。(^^;)


> 100種類あるので汎用になるように
構造体が100個あって、それをすべて KCopy.Kangen 一つに任せるというのは、
依存性が高くなりすぎて、クラス設計としてはかなり問題があると思いますし、
それでは「汎用的」では無いと思いますよ。

それぞれの型で特殊な処理が無いなら、No67058 のようにリフレクションに
任せる手もありますが、今度はタイプセーフではなくなってしまいますね。


個人的には、KCopy のようなものを用意するよりは、
データの管理も構造体自身に任せた方が良いと思います。

たとえば後述のコードのように、データ受渡し用の interface を用意しておけば、

>>> NOUGYOU 例 = new NOUGYOU();
>>> KOUGYOU 金属 = new KOUGYOU();
>>> KCopy.Kangen(Sisuoka, 例);
>>> KCopy.Kangen(Fuji, 金属);

とする代わりに、

 NOUGYOU 例 = new NOUGYOU();
 KOUGYOU 金属 = new KOUGYOU();
 例.Kangen(Sisuoka);
 金属.Kangen(Fuji);

というコードにできます。switch が不要になるので、スッキリするかと。


interface ISample
{
    void Kangen(params object[] data);
}

public struct NOUGYOU : ISample
{
    public string 米;
    public string 産地;
    public string 生産日;
    public int 金額;

    public void Kangen(params object[] data)
    {
        米 = Convert.ToString(data[0]);
        産地 = Convert.ToString(data[1]);
        生産日 = Convert.ToString(data[2]);
        金額 = Convert.ToInt32(data[3]);
    }
}

public struct KOUGYOU : ISample
{
    public string 部品;
    public string 生産日;
    public int 金額;

    public void Kangen(params object[] data)
    {
        部品 = Convert.ToString(data[0]);
        生産日 = Convert.ToString(data[1]);
        金額 = Convert.ToInt32(data[2]);
    }
}


また、データベースとの結合性の関係で、KCopy.Kangen メソッドを中継させるにしても、
 public static void Kangen(object[] DT, ISample KOUZOU)
のように、そのインターフェイスを用いて宣言できるようになります。

引用返信 編集キー/
■67071 / inTopicNo.8)  Re[6]: 構造体の使い方
□投稿者/ 裕猫 (60回)-(2013/06/28(Fri) 16:44:00)
No67068 (魔界の仮面弁士 さん) に返信
> たとえば後述のコードのように、データ受渡し用の interface を用意しておけば、
>  NOUGYOU 例 = new NOUGYOU();
>  KOUGYOU 金属 = new KOUGYOU();
>  例.Kangen(Sisuoka);
>  金属.Kangen(Fuji);
> というコードにできます。switch が不要になるので、スッキリするかと。
> のように、そのインターフェイスを用いて宣言できるようになります。
ご提示していただいたコードを参考にやってみましたところ考えたとおりの動作をさせることができました。
どのみち構造体の宣言を100通り作らねばならないのでこの方法でやるのが一番簡単ですね。
ありがとうございました。これで先が作れます。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -