Getting Started with Hydrogene (RemObjects C#)

You can download a trial version with or without the required Visual Studio. We used Virtual CloneDrive from slysoft to mount the .iso file for installation.

We have many pages of code of Oxygene on the PPS website and will use some of them as a starting point for demonstrations of C# code. All of the examples on this page are console applications for .Net.

Page Contents

Converting Oxygene to RemObjects C#

While both languages have a similar "feel", the syntax is very different. When converting Oxygene to C#, you need to be aware that C# is case sensitive and do at least the following as necessary:

C# code is not divided into interface and implementation sections, so methods (which are defined within their class), do not need a classname prefix e.g method ConsoleApp.loopsTesting; begin in Oxygene becomes public void loopsTesting(){ in C#.

Usefully, the statement separator ; is common to both languages!

Facilities of the Console

We quickly converted an introductory example demonstrating facilities of the Console from our Oxygene code.

namespace DotNetConsoleDemoCS { static class Program { public void Main(string[] args) { Console.SetWindowSize(60, 10); Console.BackgroundColor = ConsoleColor.Gray; Console.Clear(); Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("Hello World!"); Console.Beep(500, 1000); //Note of frequency 500/s for 1 s Console.ForegroundColor = ConsoleColor.DarkGreen; Console.Write("Please enter your forename: "); String forename = Console.ReadLine(); Console.WriteLine("Hello, " + forename + ". Please press a key combination such as CTRL+J." ); ConsoleKeyInfo key = Console.ReadKey(); Console.WriteLine("You pressed " + key.Key + " with " + key.Modifiers.ToString()); Console.SetCursorPosition(20, 6); Console.WriteLine("Finished!"); Console.Read(); } } }
Output to the console

This demonstration shows how to declare an Integer, Single, Char and String and how to output them to the .Net console. A screenshot of the output follows the code.

namespace console_output_cs { static class Program { public static void Main(string[] args) { int MyInt = 42; Single MySingle = 1.2345; Char MyChar = 'a'; String MyString = "This is my string."; //Note the double quotes // Output one variable at a time. Console.WriteLine(MyInt); Console.WriteLine(MySingle); Console.WriteLine(MyChar); Console.WriteLine(MyString); // Output a string composed of concatenated substrings. Console.WriteLine("Integer value: " + MyInt.ToString()); // Formatting Console.WriteLine("MyInt: {0}\nMySingle to 2dp: {1:0.00}", MyInt, MySingle); // \n gives new line Console.WriteLine(MyString); Console.ReadLine(); } } }



Input to a console program

The second demonstration shows how to input a String, Integer and Double to a .Net console program. A screenshot of the output follows the code.

namespace console_input_cs { static class InputDemo { public static void Main(string[] args) { Console.Write("Please enter a string. "); string MyString = Console.ReadLine(); Integer InputLength = MyString.Length; Console.WriteLine("You entered " + InputLength.ToString() + " characters: " + MyString); Integer intInput; Boolean accepted = false; String strInput = ""; do { accepted = false; Console.Write("Please enter an integer: "); strInput = Console.ReadLine(); accepted = Integer.TryParse(strInput, out intInput); if (accepted) Console.WriteLine(strInput + " accepted"); else Console.WriteLine("Not an integer!"); } while (!accepted); Double doubleInput; accepted = false; do { Console.Write("Please enter a real number: "); strInput = Console.ReadLine(); accepted = Double.TryParse(strInput, out doubleInput); if (accepted) Console.WriteLine(strInput + " accepted"); else Console.WriteLine("Not a number!"); } while (!accepted); Double Sum = intInput + doubleInput; Console.WriteLine("Total: " + Sum.ToString()); Console.ReadLine(); } } }



This example shows the declaration and assignment of all of the elements in a String array in a single statement. An Integer array is declared and each assignment is made after validating the input from the user. This demo introduces the foreach keyword.

namespace console_array { static class Program { public static void Main(string[] args) { Integer[] Rainfall = new Integer[12]; String[] MonthNames = new String[12]{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; Integer currentRainfall; Boolean accepted = false; Integer monthNo = 0; foreach (String month in MonthNames) { String strInput = ""; do { accepted = false; Console.Write("Please enter the rainfall for " + month + " "); strInput = Console.ReadLine(); accepted = Integer.TryParse(strInput, out currentRainfall); if (accepted) { Rainfall[monthNo] = currentRainfall; monthNo++; } else Console.WriteLine("Not an integer!"); } while (!accepted); } Console.WriteLine("You entered these values:"); foreach (Integer rain in Rainfall) Console.Write(rain.ToString() + " "); Console.ReadLine(); } } }

See below a procedure and a function with one parameter each. You must remember to append a pair of empty brackets to the identifier if the routine does not take a parameter.

namespace routines { static class Program { public void Greeting(String msg) // procedure has void return type { Console.WriteLine(msg); } public Double CircleArea(Double radius) // function { return 3.142 * radius * radius; } public static void Main(string[] args) { Greeting("Hello"); Double area = CircleArea(2.18); Console.WriteLine("Area to 2 dp: {0:0.00}", area); Console.ReadLine(); } } }

If you have mastered Pascal classes and objects, you should find it easy to use them in C#. A simple example follows. Note that instead of a Create constructor, you have a method with the same identifier as the class.

namespace classes { class Player { private String name; private Single points; public Player(String forename, Single pts) { = forename; this.points = pts; } public string GetName() { return; } public void AddPoints (Single newPoints) { this.points += newPoints; } public void ShowPoints() { Console.WriteLine("Points for " + this.GetName() + ": {0:0.0}", this.GetPoints()); } public Single GetPoints() { return this.points; } } static class Program { public static void Main(string[] args) { Player p1 = new Player("Jo", 0.0); Random rnd = new Random(); Single score = rnd.Next(1, 6); score = Convert.ToSingle(score) / 2; p1.AddPoints(score); p1.ShowPoints(); Console.ReadLine(); } } }

This demonstration is adapted from the RemObjects Oxygene .Net Loops example. The code follows a screenshot of the output.

Loops Output

using System.Linq; namespace loops_cs { public class Country { public String Name{get;set;} public String Capital{get;set;} public Country (String setName, String setCapital) { Name = setName; Capital = setCapital; } } static class ConsoleApp { private Country[] Countries = new Country[6]; public static void Main(string[] args) { fillData(); loopsTesting(); Console.ReadLine(); } public void fillData() { Countries = {new Country("UK", "London"), new Country("USA", "Washington"), new Country("Germany", "Berlin"), new Country("Ukraine", "Kyiv"), new Country("Russia", "Moscow"), new Country("France", "Paris")}; } public void loopsTesting() { //--------------------------------- //"for" loop, taking every 5th item for (Int32 i = 0 ; i <= 50; i += 5) { Console.Write(i); Console.Write(" "); } Console.WriteLine(); Console.WriteLine(); //--------------------------------- //"for" loop, going from high to low value for (Int32 i= 10 ;i > 0; i--) { Console.Write(i); Console.Write(" "); } Console.WriteLine(); Console.WriteLine(); //--------------------------------- //foreach loop Console.WriteLine("Countries: "); foreach (Country c in Countries) { Console.WriteLine(c.Name); } Console.WriteLine(); Console.WriteLine("Cities: "); Integer ind = 0; //--------------------------------- //Loop endlessly until broken out of. while (true) { Console.WriteLine(Countries.ElementAt(ind).Capital); inc(ind); if (ind == Countries.Count()) break; } Console.WriteLine(); //--------------------------------- //the type of "c" is not inferred automatically foreach (Country c in Countries) { Console.WriteLine(c.Capital + " is the capital of " + c.Name); } Console.WriteLine(); ind = 0; Console.WriteLine("Cities: "); //equivalent of "repeat ... until" loop do { Console.Write(Countries.ElementAt(ind).Capital + " "); inc(ind); } while (ind < Countries.Count()); Console.WriteLine(); ind = 0; Console.WriteLine("Countries: "); //--------------------------------- //typical "while" loop while (ind < Countries.Count()) { Console.Write(Countries.ElementAt(ind).Name + " "); inc(ind); } Console.WriteLine(); } } }
Enums and Sets

The C# equivalents of Pascal's enumerated type and set are enum and HashSet, respectively. This example demonstrates the Remove() method of a HashSet. Once you have declared a HashSet you can use IntelliSense to see other methods such as Add(), IntersectWith() and IsSubsetOf(). The code follows a screenshot of the output.

Enums and Sets Output

using System.Collections.Generic; namespace EnumsAndSets { static class Program { enum planets {Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto}; public static void Main(string[] args) { HashSet<planets> gas_giant = new HashSet<planets>(){ planets.Jupiter, planets.Saturn, planets.Uranus, planets.Neptune}; Console.Write("Position of Mercury in enum: "); Console.WriteLine(ord(planets.Mercury)); Console.Write("Successor of Mercury in enum: "); Console.WriteLine(succ(planets.Mercury)); Console.Write("Predecessor of Pluto in enum: "); Console.WriteLine(pred(planets.Pluto)); Console.Write("All of the gas giants: "); foreach (var p in gas_giant) { Console.Write(p); Console.Write(" "); } Console.Write("\nGas giants excluding Neptune: "); gas_giant.Remove(planets.Neptune); foreach (var p in gas_giant) { Console.Write(p); Console.Write(" "); } Console.WriteLine(); Console.ReadLine(); } } }
StreamWriter and StreamReader

StreamWriters and StreamReaders make it easy to save strings in files and to retrieve them, as shown in this example. The text file ints5.txt will be saved in the same folder as the executable. A screenshot of the output follows the code.

using System.IO; namespace while_file { static class Program { public static void Main(string[] args) { StreamWriter sw = new StreamWriter("ints5.txt"); for (Integer i = 1; i < 6; i++) sw.WriteLine(i.ToString()); sw.Close(); StreamReader sr = new StreamReader("ints5.txt"); String line = ""; Integer temp, sum; sum = 0; while (line != null) { line = sr.ReadLine(); Console.WriteLine(line); Integer.TryParse(line, out temp); sum += temp; } sr.Close(); Console.WriteLine("Sum: " + sum.ToString()); Console.ReadLine(); } } }


Binary Files

Using a BinaryWriter and BinaryReader enables you to save mixed data types to a binary file and to read them in the order that the data items were saved.

using System.IO; namespace random_access_dot_net_cs { static class Program { public static void Main(string[] args) { String myString = "test"; Char myChar = 'a'; //single quotes Double myDouble = 12.345; Integer myInt = 42; Int64 myInt64 = 123456789; FileStream fs; BinaryWriter bw; BinaryReader br; fs = new FileStream("test.bin", FileMode.Create, FileAccess.Write); bw = new BinaryWriter(fs); bw.Write(myString); bw.Write(myChar); bw.Write(myDouble); bw.Write(myInt); bw.Write(myInt64); bw.Close(); fs.Close(); fs = new FileStream("test.bin", FileMode.Open , FileAccess.Read); br = new BinaryReader(fs); String myString2 = br.ReadString().ToString(); Console.WriteLine(myString2); Char myChar2 = br.ReadChar(); Console.WriteLine(myChar2); Double myDouble2 = br.ReadDouble(); Console.WriteLine(myDouble2); Integer myInt2 = br.ReadInt32(); Console.WriteLine(myInt2); Int64 myInt64_2 = br.ReadInt64(); Console.WriteLine(myInt64_2); br.Close(); fs.Close(); Console.ReadLine(); } } }
Lower-Level File Handling

This program shows low-level file handling at the byte level. It creates a file, saves the ASCII codes for "ABC", then reads and outputs the saved data.

using System.IO; namespace filestream_cs { static class Program { public static void Main(string[] args) { byte[] writeBytes = new byte[3]{65, 66, 67}; FileStream fs; try { // Create new file and open it for read and write. If the file exists, overwrite it. fs = new FileStream(@"f:\test.txt", FileMode.Create); fs.Write(writeBytes, 0, 3); // Write 3 bytes from the start of the buffer. } finally { fs.Close(); } byte[] readBytes = new byte[3]; try { fs = new FileStream(@"f:\test.txt", FileMode.Open, FileAccess.Read); fs.Read(readBytes, 0, 3); } finally { fs.Close(); } foreach (byte readByte in readBytes) { char chr = System.Convert.ToChar(readByte); Console.Write(chr); } Console.ReadLine(); } } }
File finder

If you change directory to that of the executable produced by this example, the command file_find_cs *.html will instruct the program to list the filenames of html files in the current folder and in underlying folders.

/* RemObjects Oxygene Sample application converted to RemObjects C# by Norman Morrison This application lists all files in the current directory and in all subdirectories of current directory, that satisfy to the given mask. */ using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; namespace file_find_cs { static class ConsoleApp { public static String Shorten(String aFolderName ) { String result = ""; Integer lWidth = Console.WindowWidth - 1; if (aFolderName.Length > lWidth) { Int32 lStart = aFolderName.IndexOf("\\", 2); if ((lStart > 40) || (lStart == -1)) lStart = aFolderName.IndexOf("\\"); result = aFolderName.Substring(1, lStart) + "\\"; result = result + aFolderName.Substring(lWidth - result.Length); } else result = aFolderName; if (result.Length < lWidth) result = result.PadRight(lWidth); return result; } static void ProcessFolder(String aFolderName, String aMask) { String[] lFiles; Console.CursorLeft = 0; Console.Write(Shorten(aFolderName)); try { lFiles = Directory.GetFiles(aFolderName, aMask); foreach (String f in lFiles) { Console.WriteLine(); Console.WriteLine(" " + Path.GetFileName(f)); } lFiles = Directory.GetDirectories(aFolderName); foreach (String d in lFiles) { ProcessFolder(d, aMask); } } catch (DirectoryNotFoundException E) { Console.WriteLine(" " + E.Message); } catch (UnauthorizedAccessException E) { Console.WriteLine(" " + E.Message); } } public static void Main(string[] args) { Console.WriteLine("RemObjects Oxygene Filefind utility."); Console.WriteLine("Converted to RemObjects C# by Norman Morrison."); Console.WriteLine("Free and unsupported, use at your own risk."); Console.WriteLine(); if (length(args) == 1) { ProcessFolder(System.Environment.CurrentDirectory, args[0]); Console.WriteLine(); Console.WriteLine("Done."); Console.WriteLine(); } else { Console.WriteLine("Syntax"); Console.WriteLine(" Filefind <Mask>"); Console.WriteLine(); } } } }

You are unlikely to use pointers when writing managed .Net code, but we include this short demo for completeness so that you can compare the syntax with the Pascal original.

namespace PointerDemo1CS //<AllowUnsafeCode>True</AllowUnsafeCode> in project file { static class Program { public static void Main(string[] args) { Integer int1, int2; int1 = 42; unsafe { Integer* ptrInt = & int1; int2 = *ptrInt; } Console.WriteLine("int2: " + int2.ToString()); Console.ReadLine(); } } }
Accessing Data in a Firebird Database

You can set up access to Firebird with the Firebird ADO.Net Data Provider by typing the command Install-Package FirebirdSql.Data.FirebirdClient at the NuGet Package Manager prompt. If you do not have the Package Manager among your Visual Studio tools, you can install it from the NuGet home page. View the Package Manager prompt by selecting menu item Tools > NuGet Package Manager > Package Manager Console.

We give detailed instructions in the PPS database tutorial for getting started with Firebird and for creating and populating the CONTRIBUTIONS.MDB Firebird database. This RemObjects C# code accesses a copy of CONTRIBUTIONS.FDB in the root of the F drive.

using FirebirdSql; using FirebirdSql.Data; using FirebirdSql.Data.FirebirdClient; namespace FirebirdTestDotNet { static class Program { public static void Main(string[] args) { string connString = "User=student;" + "Password=pp4s;" + "Database=F:\\CONTRIBUTIONS.FDB;" + "DataSource=localhost;" + "Port=3050;"; FbConnection conn = new FbConnection(connString); conn.Open(); String sql = "SELECT Forename FROM Programmer ORDER BY Forename"; FbCommand com = new FbCommand(sql, conn); FbDataReader dr = com.ExecuteReader(); while (dr.Read()) { Console.WriteLine(dr.GetString(0)); } dr.Close(); conn.Close(); Console.ReadLine(); } } }