@Ultrakayo

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C言語の構造体をC#の構造体に移植したい

解決したいこと

C で作成したファイルをC#に移植する作業をしていますが
苦戦しております。
例)
C
work.h
struct INFO {
unsigned short id;
unsigned short No;
unsigned char error;
unsigned char rsv1;
};
sample.c
struct INFO info[64];

main.c
fo(int i=0;i<64;i++)
{
info[i].id=0xfe04;
info[i].No=i;
......
}
のようなことを
 C#で実現しようとすると
work.cs
public static struct INFO {
public static UInt16 id;
public static UInt16 No;
public static Byte error;
public static Byte rsv1;
};

main.cs
INFO[] info[64]=new INOFO(); // ??
のようになると思いますが
 このinfo[0].idを他のcsで
使用することができません。
※ 実際は info配列で ファイル操作や
 データ操作を行いたいのです
 
解決方法を教えて下さい。
追補:
struct INFO {
unsigned short id;
unsigned short No;
unsigned char error;
unsigned char rsv1;
unsigned short ch[4];
long charge[4]
};
のように さらに 配列を追加した場合は 
どうなるのでしょう?

0 likes

4Answer

このinfo[0].idを他のcsで使用することができません。

「他のcs」とは何でしょう?


【追記】

他のCSとは 同じnamespace内の他のxxx,csファイルという意味です。

work.cs ファイルで定義した構造体を、main.cs ファイルの中の main エントリーポイントで初期化して、XXX.cs ファイルの中のコードで使いたいということですか?

もしそうであれば、以下のようにしてやりたいことは実現できませんか? 以下は Visual Studio 2022 で作成した .NET 9.0 のコンソールアプリの例です。

Work.cs

namespace ConsoleApp2
{
    public struct INFO
    {
        public UInt16 id;
        public UInt16 No;
        public Byte error;
        public Byte rsv1;
    };
}

Program.cs (質問者さんの main.cs に該当)

namespace ConsoleApp2
{
    public static class Program
    {
        public static INFO[]? infoArray;

        static void Main(string[] args)
        {
            infoArray = new INFO[2];
            infoArray[0] = new INFO { id = 1, No = 10, error = 0, rsv1 = 1 };
            infoArray[1] = new INFO { id = 2, No = 20, error = 0, rsv1 = 1 };

            XXX xxx = new XXX();
            xxx.WriteValues();
        }
    }
}

XXX.cs

namespace ConsoleApp2
{
    public class XXX
    {
        public void WriteValues()
        {
            foreach (INFO info in Program.infoArray!)
            {
                Console.WriteLine($"id: {info.id}, No: {info.No}, error: {info.error}, rsv1: {info.rsv1}");
            }
        }
    }
}

実行結果

result.jpg

1Like

Comments

  1. 他のCSとは 同じnamespace内の他のxxx,csファイルという意味です。

    work.cs ファイルで定義した構造体を、main.cs ファイルの中の main エントリーポイントで初期化して、XXX.cs ファイルの中のコードで使いたいということですか?

    もしそうであれば、以下のようにしてやりたいことは実現できませんか? 以下は Visual Studio 2022 で作成した .NET 9.0 のコンソールアプリの例です。

    Work.cs

    namespace ConsoleApp2
    {
        public struct INFO
        {
            public UInt16 id;
            public UInt16 No;
            public Byte error;
            public Byte rsv1;
        };
    }
    
    

    Program.cs (質問者さんの main.cs に該当)

    namespace ConsoleApp2
    {
        public static class Program
        {
            public static INFO[]? infoArray;
    
            static void Main(string[] args)
            {
                infoArray = new INFO[2];
                infoArray[0] = new INFO { id = 1, No = 10, error = 0, rsv1 = 1 };
                infoArray[1] = new INFO { id = 2, No = 20, error = 0, rsv1 = 1 };
    
                XXX xxx = new XXX();
                xxx.WriteValues();
            }
        }
    }
    

    XXX.cs

    namespace ConsoleApp2
    {
        public class XXX
        {
            public void WriteValues()
            {
                foreach (INFO info in Program.infoArray!)
                {
                    Console.WriteLine($"id: {info.id}, No: {info.No}, error: {info.error}, rsv1: {info.rsv1}");
                }
            }
        }
    }
    

    実行結果

    result.jpg

  2. @Ultrakayo

    Questioner

    ありがとうございます。試してみます。

  3. どのように試したかとその結果を書いてください。

C#のトップレベルステートメントは暗黙的にエントリポイントとしてメソッドを生成するので,そのままでは変数を外から見ることはできません.

なのでstaticにするか,本当にその配列をどこからでも見える位置に置いておく必要があるか検討する必要はあります.

1Like

Comments

  1. そもそも例示されたCとC#のソースだと意味が違ってきますね.
    こちらどうぞ.

  2. @Ultrakayo

    Questioner

    早速の投稿ありがとうございます。
    ちょっと 検討してみます。
    ご教示ありがとうございました。

  3. @Ultrakayo

    Questioner

    そもそも例示されたCとC#のソースだと意味が違ってきますね.
    こちらどうぞ.
    C言語の方は H8(ルネサス)で作成したものでメモリーに直接書き込んでいます
    その意味では C#のほうは ちがうのですが.....
     

  4. C言語の方は H8(ルネサス)で作成したものでメモリーに直接書き込んでいます
    その意味では C#のほうは ちがうのですが.....

    そこではなくて,そもそもメンバをstaticにしてしまってるのでvalue.idというようにインスタンス的に使うことができません.

  5. @Ultrakayo

    Questioner

    そうなんですか Staticは 万能かと 思いました。

  6. @Ultrakayo

    Questioner

    メンバ変数にstaticをつけた場合、その変数がメモリ上のどこに配置されるかが固定される。メモリ上の位置が固定されるということはどこからでもこの変数にアクセスできるということです。と 記述されていましたので....

  7. @Ultrakayo

    Questioner

    public struct INFO {
    public UInt16 id;
    public UInt16 No;
    public Byte error;
    public Byte rsv1;};
    INFO[] info = new INFO[64];にしたら
    info[0].id=0が可能になります。
    Staticを使うと 初期化しなくても 直接
    制御できると 書いてあったので...
    どうにか 使えそうです。
    ありがとうございました。
      

Comments

  1. global usingでいいのではないでしょうか
    これはプロジェクト全体で名前空間を共有するための宣言子です
    まず構造体と静的メンバを用意します

    public struct INFO {
    	public UInt16 id;
    	public UInt16 No;
    	public Byte error;
    	public Byte rsv1;
    }
    
    namespace INFODesigner{
    	public class INFOManager{
    		public static INFO[] info=[new(),new(),new()];
    	}
    }
    

    続いて名前空間を宣言します
    using staticを使用することで、静的メンバに対して完全修飾名を省略できます
    以下は使用例です

    global using static INFODesigner.INFOManager;
    using System;
    					
    public class Program
    {
    	public static void Main()
    	{
    		Console.WriteLine(info[0].id);
    	}
    }
    
    0
    

    global using staticで静的メンバのプロジェクト内共有を宣言します
    global using宣言がまとめられた.csファイルもあるので、そちらにglobal using static 名前空間.クラス名;のフォーマットで宣言を追加するといいでしょう

    これで期待された動作になるはずです

  2. 質門者さんは C 言語では extern を使って異なるファイル間で変数を共有していて、C# 言語でそれに代わる解決方法を知りたいというのが質問の趣旨ではないかと想像しています。

    そうだとすると、global using では解決できないと思うのですが、いかがですか?

  3. extern を使って異なるファイル間で変数を共有していて

    であればなおさらglobal usingが適格かと思うのですが、ファイル間共有の他にも用途があるのでしょうか?

    externはライブラリとのリンクを想定するキーワードですから、そうであればC#では.csproj

    <ItemGroup>
        <Reference Include=“.dllPath”>
        </Reference>
    </ItemGroup>
    

    を設けて、改めてglobal usingを加えることで同等の操作になるはずです
    また名前空間はライブラリの内外問わず拡張可能なので、後から新しいグローバル変数のようなものを新しいクラスの静的メンバとして同一名前空間内に追加構築できます

    グローバル領域はC#から見れば静的領域の一部ですから、staticメンバとして扱うべきかと思います

  4. 尚「名前空間を共有する」とは、以下のように定義を分割することを指します

    info.cs
    public struct INFO {
    	public UInt16 id;
    	public UInt16 No;
    	public Byte error;
    	public Byte rsv1;
    }
    
    namespace INFODesigner{
    	public class INFOManager{
    		public static INFO[] info=[new(),new(),new()];
    	}
    }
    
    global.cs
    global using static INFODesigner.INFOManager;
    global using System;
    
    Program.cs
    public class Program
    {
    	public static void Main()
    	{
    		Console.WriteLine(info[0].id);
    	}
    }
    
  5. 質問者さんの問題は、以下のように定義し初期化した構造体の配列の参照 infoArray を、

    namespace ConsoleApp2
    {
        public static class Program
        {
            public INFO[] infoArray;
    
            static void Main(string[] args)
            {
                infoArray = 初期化のコード;
            }
        }
    }
    

    別ファイル XXX.cs 内のコートでどうやって取得するかという話だと思います。違いますか?>質問者さん

    namespace ConsoleApp2
    {
        public class XXX
        {
            public void WriteValues()
            {
                // infoArray をどうやって取得する?
            }
        }
    }
    

    だとすると、それは名前空間で解決できる問題ではないはずです。

    上の問題(構造体の配列の参照を XXX にどのように渡すか)を解決するには、私の回答に書いたように infoArray を static として宣言し、XXX.cs では Program.infoArray というようにして取得するか、XXX クラスのコンストラクタに INFO[] 型を引数とするものを追加し、XXX クラスのインスタンスを作る際そのコンストラクタを使って構造体の配列の参照を引数を通じて渡すということになると思いますが、いかがですか?> @blonz3977 さん

  6. 質問者さん>

    上の私のコメントを見てもらえましたか? 本質的なところは名前空間の問題ではなくて、C# 10.0 以降でしか使えない global using は使わなくても解決できるはずですよ。

  7. Linux上なので AnyCPUで作成しております。言語バージョンを選択できないみたいです

    Linux上でWindows環境を構築しているという状況でしょうか?
    開発環境をLinuxに移すことは可能ですか?
    またC#7.3ということは使用しているプロジェクトは.NetFreamworkですか?
    あるいはlinuxターゲットなのでmonoでしょうか?

    まあAnyCPUがあるということなのでPratformTargetオプションが存在するかと思いますが、であれば環境は.NetCoreかと思います
    この仮定を前提とした場合、C#の言語バージョンを変更する手段は二つあります

    .csprojを編集する

    .csprojTargerFreamworkオプションを編集して、言語バージョンを変更します

    <PropertyGroup>
        <TargetFreamwork>net9.0</TargetFreamwork>
    

    dotnetコマンドでプロジェクトを構成する

    C#にはlinux用の開発環境が存在します
    linux上でこの環境をインストールし、C#7.0以上の最新バージョンの機能を利用することが可能です

    dotnetコマンドの機能はVisual Studio相当なので、こちらで移植をやり直すのも手段の一つです

  8. @blonz3977 さん>

    上の私のコメントを見てもらえましたか? 本質的なところは名前空間の問題ではなくて、C# 10.0 以降でしか使えない global using は使わなくても解決できるはずですよ。

  9. 定義し初期化した構造体の配列の参照

    初期化であればstaticコンストラクタを使う手段もあります

    namespace INFODesigner{
    	public class INFOManager{
    		public static INFO[] info=new INFO[3];
    
            static INFOManager(){
                info[0]=new INFO();
                info[1]=new INFO();
                info[2]=new INFO();
          }
    	}
    }
    

    あるいは以下のような属性を使う方法もあります

    [ModuleInitializer属性]
    https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.moduleinitializerattribute?view=net-8.0

  10. @blonz3977 さん>

    初期化であればstaticコンストラクタを使う手段もあります

    論点がずれてませんか? それより先にコメントした「本質的なところは名前空間の問題ではなくて、C# 10.0 以降でしか使えない global using は使わなくても解決できる」ということにフィードバックをお願いします。

  11. 宣言 どうも共有できるみたいです。

    そうですね、.csごとに名前空間を宣言するスタイルならばusing staticを逐次宣言するスタイルでも良いかと思います
    いずれにせよグローバル変数を模倣するならばstaticメンバを使った方がいいです
    別に以下のように初期化しても問題はないはずなので

    namespace INFODesigner{
    	public class INFOManager{
    		public static INFO[] info=Init(new INFO[3]);
    
            static INFO[] Init(INFO[] info){
               //初期化処理
               return info;
          }
    	}
    }
    
  12. 質問者さん>

    先にコメントした「本質的なところは名前空間の問題ではなくて、C# 10.0 以降でしか使えない global using は使わなくても解決できる」ということにフィードバックをお願いします。

    実際 中身まで 使用できるかは これから試します

    私の回答を見てもらってますか? 回答のコードで中身も共有できることは示しています。

    私の回答ベースで Q&A を続ける意向があれば、Q&A の場所を私の回答の下のコメント欄に移してください。それから、質問はクローズしないでオープンのままにしておいてください。

  13. メソッドで初期化するパターンも掲載しておきます(C#7.3準拠)

    using System;
    using static INFODesigner.INFOManager;
    					
    public class Program
    {
    	public static void Main()
    	{
    		Console.WriteLine(info[0].id);
    	}
    }
    
    public struct INFO {
    	public UInt16 id;
    	public UInt16 No;
    	public Byte error;
    	public Byte rsv1;
    }
    
    namespace INFODesigner{
    	public class INFOManager{
    		public static INFO[] info=Init(new INFO[3]);
    
            static INFO[] Init(INFO[] info){
                info[0]=new INFO();
                info[1]=new INFO();
                info[2]=new INFO();
    			return info;
            }
    	}
    }
    
    0
    

よく分からないけど、こんな感じの事がしたいのかな?

using System;

public struct INFO
{
    public UInt16 id;
    public UInt16 No;
    public Byte error;
    public Byte rsv1;

    public readonly static INFO[] SharedArray = new INFO[64];
}

public class Program
{
    public static void Main()
    {
        var infos = INFO.SharedArray;
        for(UInt16 i=0; i<infos.Length; i++)
        {
            infos[i].id = 0xfe04;
            infos[i].No = i;
        }
    }
}

INFO内で初期化も完結させるなら、下のような感じでいけます。

public struct INFO
{
    public UInt16 id;
    public UInt16 No;
    public Byte error;
    public Byte rsv1;

    public readonly static INFO[] SharedArray = CreateSharedArray();

    private static INFO[] CreateSharedArray()
    {
        var infos = new INFO[64];
        for (UInt16 i = 0; i < infos.Length; i++)
        {
            infos[i].id = 0xfe04;
            infos[i].No = i;
        }
        return infos;
    }
}

あとは、どのクラスからでも INFO.SharedArray で初期化済み配列にアクセスできます。

0Like

Comments

  1. @Ultrakayo

    Questioner

    皆様からの投稿ありがとうございました。
    pinfo.cs
    using static CWinFormsApp.workClass;
    namespace CWinFormsApp
    {
    internal class PinfoClass
    {
    public static PInfoClass[] pinfo = new PInfoClass[64];
    }
    }

    workClass.cs
    public struct PInfoClass
    {
    public UInt16 id;
    public UInt16 No;
    public Byte cf;
    }
    program.cs
    using static CWinFormsApp.PinfoClass;
    を使って 解決しました。
     追補:コーディングはできましたが DEBUGすると まだ ちょっと ダメみたい  

  2. CWinFormsApp という名前からすると Windows Forms アプリ? Windows Forms は Windows API(GDI/GDI+)をマネージコードでラップし、Windows のユーザーインターフェイス要素へのアクセスを提供するアプリケーションフレームワーク。RaspBerry pi で動くんですか?

  3. @Ultrakayo

    Questioner

    @radian-jpさんに 質問ですが

    public struct INFO
    {
    public UInt16 id;
    public UInt16 No;
    public Byte error;
    public Byte rsv1;
    public readonly static INFO[] SharedArray= CreateSharedArray();

    private static INFO[] CreateSharedArray()
    {
    var infos = new INFO[64];
    for (UInt16 i = 0; i < infos.Length; i++)
    {
    infos[i].id = 0xfe04;
    infos[i].No = i;
    }
    return infos;
    }
    }
    struct INFO の中に
    public static Byte[] ch= new Byte[4];
    public static Int16[] chage= new Int16[4];
    のような 配列を作りたい場合は
    public struct INFOA{
     public Byte ch;
     public Int16 charge;
    }
    を作ってpublic readonly static INFOA[] SharedArray= CreateSharedArray();
    さらに..... どうなるのでしょうか?

  4. 何がしたいのかさっぱり判らなくて回答できないです。別にC言語でも構いませんが、コードの目的を説明し、変数名も判りやすく付けるなりして、人に読ませる工夫をしてください。
    あと、インデントされていなくて読みづらいのでコードブロックを使ってください。

    あと、C#での構造体中の配列のメモリ配置は、C言語のそれとは違うので注意してください。

  5. @Ultrakayo

    Questioner

    ありがとうございました。

Your answer might help someone💌