Alternative to #include files when using C#

  • C#
  • Thread starter berkeman
  • Start date
  • Tags
    files
In summary, C# does not have a mechanism for #include files, so the developer needs to either use a library or include the code manually.
  • #1
berkeman
Mentor
68,275
21,968
I'm using C# to write some code for a manufacturing test of an embedded system device. It will access several instruments that are making measurements on the device, including some National Instruments MIO channels and a Tektronix power analyzer.

I'm most familiar with C and Tck/Tk, and am learning C# as I go. I see that there is no mechanism for #include files in C#, and have been reading about the alternatives like here:

https://forum.unity3d.com/threads/c-and-the-best-equivalent-of-include-from-c-c.258241/

I definitely need to be able to pull my configuration information out into separate files, preferably with separate files for each instrument that I'll be controlling. What would you recommend as the best/easiest way to do this in C#?

Here is an example of a single C# console app file that controls some power relays that are part of the test setup. I put the configuration information into the main file for now, until I learn how to break that info out into a separate file. Thanks for your help.

C:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Threading;

namespace SimpleRelayControl1
{
    class Program
    {
        static SerialPort serialPort2;
        static Byte[] r1on  = new Byte[6] { 0xAA, 0x03, 0xFE, 0x6C, 0x01, 0x18 };
        static Byte[] r1off = new Byte[6] { 0xAA, 0x03, 0xFE, 0x64, 0x01, 0x10 };
        static Byte[] r2on  = new Byte[6] { 0xAA, 0x03, 0xFE, 0x6D, 0x01, 0x19 };
        static Byte[] r2off = new Byte[6] { 0xAA, 0x03, 0xFE, 0x65, 0x01, 0x11 };
        static Byte[] r3on  = new Byte[6] { 0xAA, 0x03, 0xFE, 0x6E, 0x01, 0x1A };
        static Byte[] r3off = new Byte[6] { 0xAA, 0x03, 0xFE, 0x66, 0x01, 0x12 };
        static Byte[] r4on  = new Byte[6] { 0xAA, 0x03, 0xFE, 0x6F, 0x01, 0x1B };
        static Byte[] r4off = new Byte[6] { 0xAA, 0x03, 0xFE, 0x67, 0x01, 0x13 };
        static Byte[] allon = new Byte[6] { 0xAA, 0x03, 0xFE, 0x82, 0x01, 0x2E };
        static Byte[] alloff = new Byte[6] { 0xAA, 0x03, 0xFE, 0x81, 0x01, 0x2D };

        static string inputString = "";

        static void Main(string[] args)
        {
            // Define COM port for Relay control
            serialPort2 = new SerialPort();
            serialPort2.Close();
            serialPort2.PortName="COM4";
            serialPort2.Encoding = System.Text.Encoding.GetEncoding("Windows-1252");
            serialPort2.BaudRate=115200;
            serialPort2.DataBits = 8;
            serialPort2.Parity = Parity.None;
            serialPort2.StopBits= StopBits.One;
            serialPort2.Handshake= Handshake.None;
            serialPort2.Open();

            while(inputString != "exit")
            {
                Console.Write("Relay> ");
                inputString = Console.ReadLine();
                switch(inputString)
                {
                    case "r1on" :
                        serialPort2.Write(r1on, 0, 6);
                        break;
                    case "r1off" :
                        serialPort2.Write(r1off, 0, 6);
                        break;
                    case "r2on":
                        serialPort2.Write(r2on, 0, 6);
                        break;
                    case "r2off":
                        serialPort2.Write(r2off, 0, 6);
                        break;
                    case "r3on":
                        serialPort2.Write(r3on, 0, 6);
                        break;
                    case "r3off":
                        serialPort2.Write(r3off, 0, 6);
                        break;
                    case "r4on":
                        serialPort2.Write(r4on, 0, 6);
                        break;
                    case "r4off":
                        serialPort2.Write(r4off, 0, 6);
                        break;
                    case "allon":
                        serialPort2.Write(allon, 0, 6);
                        break;
                    case "alloff":
                        serialPort2.Write(alloff, 0, 6);
                        break;
                }
            }
            serialPort2.Close();
        }
    }
}
 
Technology news on Phys.org
  • #3
Thanks @Filip Larsen I'll give that a try. Can I still name my supporting files *.h, or do they need to be *.cs files as well?
 
  • #4
berkeman said:
Can I still name my supporting files *.h, or do they need to be *.cs files as well?

Since you are going to reformat your file content anyway if you are going to use partial classes for this (since you need your "configuration" inside at least a namespace and a partial class definition in each file) I would recommend going with using cs-files. Even if it is possible using *.h file for C# it is most likely going to confuse the poor humans involved. :wink:

If you have a lot of such configuration files in a simple format and are reluctant to reformat it to C# format, then perhaps instead you can parse them and generate the C# files at compile time (e.g. transpilation), or parse them at runtime during start-up as regular data. Once you start to treat the configuration as data there is no end to how fancy you can make it or how many libraries and tools you can get to help you.
 
  • Like
Likes jim mcnamara and berkeman
  • #5
Thanks @Filip Larsen that worked. I used the *.CS extension, and for now have the extra file(s) in the same directory as the Main program. I used the same Namespace in each file, and the same Partial Public class name.

The only trick that it took me a few minutes to figure out is that it works best to add a new blank C# Class file to the Project first, and then copy the code into it. I first tried creating the file with the code and then trying to associate it as a Class file with the Project, but wasn't able to figure that out in Visual Studio. There probably are ways to do it, but I'll stick with adding the new blank Class files to the Project first, and then putting code into them.

Thanks for the tips! :smile:
 
  • #6
Often one uses serialization to accomplish these kind of things (configurations and settings). In fact I'm planning to do a similar thing to load messages, states etc; used in a protocol I'm meant to implement.
Often one uses xml serialization but in this case binary might be equally if not better suited.
Since the advantage of easily editable xml is moot with most byte-values (imho) although this link seems to suggest they can be stored in a hexadecimal representation.

It seems like byte arrays are stored as base64 strings which could be generated by you.

I however would likely build a simple tool to generate the xml files, that way you get the best of both worlds and an easily extendable solution (e.g. use a list of byte arrays and create a generic class to run the tests taking an xml file in it's constructor)

You then make a class e.g.

Code:
public class InstrumentName {
    public byte[6] rOn {get; set;}
    public byte[6] rOff {get; set;}
}

By decorating this with suitable attributes you'd be able to store the classes as xml and read them when needed.
 
  • Like
Likes berkeman

FAQ: Alternative to #include files when using C#

What are alternative ways to include files when using C#?

There are several alternative ways to include files when using C# such as using the "using" directive, referencing external assemblies, using the "extern alias" keyword, and using the Assembly class to load and execute code from external assemblies.

How does the "using" directive work?

The "using" directive allows you to specify the namespace of a class or type that you want to use in your code. This eliminates the need to fully qualify the name of the class every time it is used in your code.

What is the purpose of referencing external assemblies?

Referencing external assemblies allows you to access classes, methods, and other types from external libraries in your code. This is useful when you want to use functionality from a library that is not included in the default C# libraries.

What is the "extern alias" keyword used for?

The "extern alias" keyword allows you to reference multiple versions of the same assembly in your code. This is useful when you have different versions of a library that you want to use in your project.

How can the Assembly class be used to load and execute code from external assemblies?

The Assembly class provides methods for loading and executing code from external assemblies. You can use the Load method to load an assembly at runtime and then use reflection to access and execute code from that assembly.

Similar threads

Replies
7
Views
4K
Replies
7
Views
2K
Replies
75
Views
5K
Replies
19
Views
4K
Replies
6
Views
2K
Replies
8
Views
6K
Replies
1
Views
1K
Replies
3
Views
2K
Back
Top