Is C# easier to learn than C++?

It’s free for Open source projects. ( you can confirm that part)

I don’t think we actually qualify

as we have plans for receiving money and paying core developers. So I’d actually would have to pay out of pocket for it, though I might try the trial again if they let me, and if it is nice enough buy it.

Sure, give it a try. Currently core developers aren’t being paid. So we are eligible for at least a year.

Ok, I’m going to share my C# learning, which I’m restarting from scratch. That way, more experienced people might give useful advice or maybe just criticize the source, which is tutorialspoint by the way.

https://www.tutorialspoint.com/csharp/index.htm

Environment

C# is based on the .Net framework from MS and people use VS or Mono as an IDE. I’ll just stick with Notepad++ and use cmd for compiling.

csc programname.cs
Apparently, it’s recommended to name a file after its class. Leads to further questioning in my head.

Program Structure

using System; // includes the System namespace

namespace App
{
   class HelloWorld
   {
      
      static void Main(string[] args) // Main = program entry point
      {
         // Single-line
         /* multi-line */
         Console.WriteLine("Hello World!");
         Console.ReadKey(); // prevents autoclose
      }
   }
}

Questions!

  1. What is the string[] args thing?
  2. If a file has a namespace instead of a single class, do I name the file after its namespace instead?

Basic Syntax

using System;

namespace RectangleApplication
{
   
   class Rectangle
   {
      // fields
      int length, width;
      
      public void AcceptDetails() //method
      {
         length = 4;
         width = 5;
      }
      
      public int GetArea()
      {
         return length * width; 
      }
   }
   
   class ExecuteRectangle
   {
      static void Main()
      {
         Rectangle r = new Rectangle();
         r.AcceptDetails();
         Console.WriteLine(r.GetArea());
         Console.ReadKey();
      }
   }
}
  1. Now, do I name the file rectangle.cs or program.cs, hmmm?

Ok, so a class is just a template. Perfect. So I need to create an object from it like so:
Class obj = new Class();

And to access a method or a field:
obj.fieldName;
obj.Method();

And a namespace is a collection of classes.

To be continued…

I don’t know C#, but I’m pretty sure args contains the command line arguments.
Let’s say you wanted to list the contents of directory my_dir like this:

ls my_dir

or (in windows if i’m not wrong):

dir my_dir

ls (or dir) will be run. But how does it know what directory it should read? It will read args to see what command line arguments it received. It sees “my_dir” and then knows what directory it should list. This applies to other programs that read command line arguments.
Edit: Maybe this answer will explain it better: c# - What is "string[] args" in Main class for? - Stack Overflow

2 Likes

not a expert but

  1. Elrakrez already explained above
  2. The name of the file really does not matter, but you should name it after the main class
  3. You would ideally separate it into 2 files but i think in this case it would be called rectangle.cs

also brackey got a good video tutorials if like those:

I guess that’s a fine enough starting point, though as an expert reading through the Tutorialspoint tutorials I’ve found multiple errors. With that C# tutorial, I was able to find a mistake in just a few minutes. They incorrectly talk about destructors running when things go out of scope, which is entirely wrong. Finalizers (as they are properly called in garbage collected languages), actually only run when the garbage collector notices that the object is unreferenced and should be deleted. Microsoft’s documentation says that: Finalizers - C# Programming Guide - C# | Microsoft Learn And one final point is that the Dispose pattern should basically always be used over finalizers.

I guess if you can work through the entire tutorial, it will be a good starting point, even with the mistakes, which you can then build on.

It’s much better for organization to name files after what classes they contain. For Thrive we are using stylecop which will not let your code pass if you don’t name your files after the classes they contain. This is why you should really use an IDE for C# as it can automatically add new files to the project definitions for it to be compiled.

They are the command line parameters passed to your program. For example git pull master would receive the pull master part as args (and possibly the exe name that is running, though I haven’t checked the C# documentation so I don’t know, at least in C++ the first argument is always the running executable name, then followed by the command line parameters).

No, because namespaces are supposed to span multiple files. So if you have a folder called Utilities with multiple files in it, all of them should define their classes in the Utilities namespace. Once again this is a good naming practice to organize large programs.

Files should not usually contain multiple classes. Though, I make exceptions to this rule for closely related classes like having a small interface and the implementation in the same file. But in this case you should have Rectangle.cs and Program.cs separately. And the class containing Main should be named Program.

1 Like

Data Types

Three branches: Value, Reference and Pointer types.

Values types are assigned a value directly.

type Type Size (in bit) Signed? Number of decimals Range Default Value
bool Boolean 8 true or false false
char Unicode character 16 [U+0000, U+FFFF] ‘\0’
byte Integer 8 [0, 255] 0
sbyte Integer 8 [-128, 127] 0
ushort Integer 16 [0, 65.5k] 0
short Integer 16 [-32.7k, 32.7k] 0
uint Integer 32 [0, 4.29B] 0
int Integer 32 [-2.1B, 2.1B] 0
ulong Integer 64 [0, 18.4qi] 0
long Integer 64 [-9.2qi, 9.2qi] 0L
float Floating point 32 6-9 [1.5 \times 10-45, 3.4 \times 1038] 0.0F
double Floating point 64 15-17 [5.0 \times 10-324, 1.7 \times 10308] 0.0D
decimal Floating point 128 28-29 [1.0 \times 10-28, 7.9 \times 1028] 0.0M

Apparently, float and double are binary coded and decimal is not.

It’s possible to determine the size of a type with the sizeof() method, which outputs the answer in bytes.

Console.WriteLine(sizeof(byte)); // outputs 1

Reference types refer to a memory location and come in three built-in types : object, dynamic and string.

Object types can be assigned any value just like dynamic types, except that the former has type checking at compile time, and the latter at run time.

object obj;
obj = 100;
dynamic dyn = 15;

String type can be assigned string values.

String str "Hello";
@"World";

And pointer types refer to a memory location. All those types don’t seem useful for now, given how little explanation they provide. By the way, aren’t pointers a reference type?

Type Conversion

Two ways: Implicit and Explicit.

Implicit conversion converts a smaller type to a greater one. Rules:

  • OK Integer ----> Floating point
  • NOT OK Integer <------ Floating point
  • NOT OK float/double -------> decimal (binary, not binary eh)
  • NOT OK signed Integer --------> unsigned Integer
  • Can’t convert to the same size (byte can’t convert to sbyte)
  • Can only convert if within the right range

Explicit conversion works with methods. Name convention for methods: ToType(). However, to convert to float, one needs to use the ToSingle() method. For integers (except bytes), just use ToInt<size in bit>for the corresponding signed integer type or ToUInt<size>if unsigned.

int a = 20;
a.ToString();

Variables

Eh, variables just store a value within a memory location.

int a, b; OK
int a, b = 5, 10; NOT OK

You can also accept user input using the ReadLine() method.

int a = Convert.ToInt32(Console.ReadLine());

1 Like

No, C# actually has real pointers, like C++, to raw memory. Because they are unsafe and you can crash your program with them, you can only use them inside unsafe blocks.

Not much to add but I appreciate your dedication by making a real table

how do you even do that?

|Column A|Column B|Column C|
|---|---|---|
|a|b|c|
1 Like

Constants

Constants are like variables except you can define their value only once. So technically, not variables.

Constants and variables…

— Elizabeth Comstock, Bioshock Infinite

To define a constant:

const <type> name = value;

Like:
const double pi = 3.1416;

Operators

Eh, operators.

Arithmetic Operators

+, -. *, /, … ah I already know them!

Relational Operators

==, !=. >, <, >=, etc.

Logical Operators

Operator Description
&& AND
`
!(condition) NOT

Bitwise Operators

New one! They operate on bits, which means binary operations. Examples are in the source.

Operator Name Description
& AND Copies a bit from both operands.
` ` OR
^ XOR Copies a bit exclusively from either operands.
~ Complement Operator Reverses bits (0 becomes 1 and vice versa). Good for converting negative numbers.
<< Left Shift Pushes bits to the left by a number of bits.
>> Right Shift Pushes bits to the right by a number of bits.

@hhyyrylainen, are they used in Thrive? If they are, which file is it?

And also, when do they become useful?

Skipping assignement operators because they are self-explanatory. As for misc operators, meh. Priority of operators is a long table. I might edit this post, but I don’t know.

Decision Making

if, if else, else if. if else can be replaced by ternary operator like so:

string str = (a >= b) ? "Good." : "Bad.";
Console.Write(str);

Fun fact: If you were to draw a schematics of an else if, you would realize that it looks like a parallel circuit in electronics.

Switch decision making

int num = value;

switch(num)
{
    case 1:
        code;
        break;
    case 2:
        code;
        break;
    case 3:
        code;
        break;
    default:
        code;
        break;
}

Switch statements compare a variable to several values and executes a code accordingly. Otherwise, it executes the default code if specified.

2 Likes

switch statement can also have multiple cases go to the same outcome like this:

int num = value;

switch(num)
{
    case 1:
    case 99:
        code;
        break;
    case 2:
    case 98:
        code;
        break;
    case 3:
    case 97:
        code;
        break;
    case 0:
    default:
        code;
        break;
}
1 Like

I don’t think so. Though, we might use them in the future for packing a bunch of flag bits in a single int, which was used when the game was in C++, but so far there hasn’t been a need to do that in the C# version.

They are useful to manipulate data within individual bytes. Let’s say you wanted toggle the bit at position b in my_byte, you’d do my_byte ^ 1 << b. If b is 2 and my_byte is 0b10101010, you’d get 0b10101110 in this operation (1 << 2 is 0b00000100).

As hhyyrylainen mentioned, it can be used to store or read flags in a header of some file format.
A program reading a file could do something like this (this is in C, going to probably look the same, but I’m just saying it just to be safe in case the code would look different):

// Do stuff if bit 6 is set.
if (flags & 1 << 6) // or (flags & 0b01000000)
  {
    do_stuff()
  }

I don’t really understand what this is but I guess I’ll understand later on.

Ok, so today I’m going to tackle the loops chapter, which is generally the point where I quit learning. Hopefully, I won’t lose my motivation and will go on to learn more. I’m also going to try and create a simple calculator. After that, I may continue learning.

Loops

While Loops

while (a < b)
{
    code;
}

When are they more useful than for loops?

For Loops

for (i = 0; i < 6; i++)
{
    code;
}

do…while Loops

Unlike the while loop, it executes the code right before starting the loop.

do
{
    code;
} while (a < b);

break Statement

The break; statement allows one to terminate a loop, generally when reaching a particular condition (if statement within a loop).

continue Statement

The continue; statement skips the current iteration and goes to the next one.

When you don’t know how many times you are looping, or the condition is a boolean variable, they are the only loop type that makes sense.

I built the calculator.

calculator
program.cs
using System;

namespace Calculator
{
	class Program
	{	
		static void Main(string[] args)
		{	
			double num1, num2;
			num1 = 0.0;
			num2 = 0.0;
			
			while(true)
			{
				Console.WriteLine("This is a calculator. Type 'list' to see all possible operations.");
				string choice = Console.ReadLine();
				
				switch(choice)
				{
					case "1":
						
						Console.WriteLine("You have chosen 'Addition'.\nPlease enter the first number if necessary...");
						/*The user has to enter the numbers first, then the code will take care of the operation.
						If the user has not cleared the previous result, they won't have to enter the first number.*/
						
						Arithmetics addition = new Arithmetics();
						num1 = (num1 == 0.0) ? Convert.ToDouble(Console.ReadLine()) : num1; // if num1 has been cleared, enter the number. Otherwise, keep the same value.
						
						Console.WriteLine("Now, please enter the second number...");
						num2 = Convert.ToDouble(Console.ReadLine());
						
						addition.Addition(num1, num2);
						Console.WriteLine("The result is {0}", addition.result);
						num1 = addition.result;
						
						continue;
					
					case "2":
					
						Console.WriteLine("You have chosen 'Subtraction'.\nPlease enter the first number if necessary...");
						
						Arithmetics subtraction = new Arithmetics();
						num1 = (num1 == 0.0) ? Convert.ToDouble(Console.ReadLine()) : num1;
						
						Console.WriteLine("Now, please enter the second number...");
						num2 = Convert.ToDouble(Console.ReadLine());
						
						subtraction.Subtraction(num1, num2);
						Console.WriteLine("The result is {0}", subtraction.result);
						num1 = subtraction.result;
						
						continue;
					
					case "3":
					
						Console.WriteLine("You have chosen 'Multiplication'.\nPlease enter the first number if necessary...");
						
						Arithmetics multiplication = new Arithmetics();
						num1 = (num1 == 0.0) ? Convert.ToDouble(Console.ReadLine()) : num1;
						
						Console.WriteLine("Now, please enter the second number...");
						num2 = Convert.ToDouble(Console.ReadLine());
						
						multiplication.Multiplication(num1, num2);
						Console.WriteLine("The result is {0}", multiplication.result);
						num1 = multiplication.result;
						
						continue;
					
					case "4":
					
						Console.WriteLine("You have chosen 'Division'.\nPlease enter the first number if necessary...");
						
						Arithmetics division = new Arithmetics();
						num1 = (num1 == 0.0) ? Convert.ToDouble(Console.ReadLine()) : num1;
						
						Console.WriteLine("Now, please enter the second number...");
						num2 = Convert.ToDouble(Console.ReadLine());
						
						division.Division(num1, num2);
						Console.WriteLine("The result is {0}", division.result);
						num1 = division.result;
						
						continue;
					
					case "5":
					
						Console.WriteLine("You have chosen 'Modulus'.\nPlease enter the first number if necessary...");
						
						Arithmetics modulus = new Arithmetics();
						num1 = (num1 == 0.0) ? Convert.ToDouble(Console.ReadLine()) : num1;
						
						Console.WriteLine("Now, please enter the second number...");
						num2 = Convert.ToDouble(Console.ReadLine());
						
						modulus.Modulus(num1, num2);
						Console.WriteLine("The result is {0}", modulus.result);
						num1 = modulus.result;
						
						continue;
					
					case "list":
						Console.WriteLine(" 1-Addition \n 2-Subtraction \n 3-Multiplication \n 4-Division \n 5-Modulus: Find the remainder of a division. \n clear-Clear the previous result. \n exit-Exit the program.");
						continue;
					
					case "clear":
						num1 = 0.0;
						Console.WriteLine("The result has been cleared.");
						continue;
					
					case "exit":
						Console.WriteLine("Please press <Enter> to exit...");
						Console.ReadKey();
						break;
					
					default:
						Console.WriteLine("Wrong input. Please enter a valid input.");
						continue;
				}
				break;
			}
		}
	}
}
arithmetics.cs
using System;

namespace Calculator
{
	class Arithmetics
	{
		public double result;
		
		public double Addition(double number1, double number2)
		{
			result = number1 + number2;
			return result;
		}
		
		public double Subtraction(double number1, double number2)
		{
			result = number1 - number2;
			return result;
		}
		
		public double Multiplication(double number1, double number2)
		{
			result = number1 * number2;
			return result;
		}
		
		public double Division(double number1, double number2)
		{
			result = number1 / number2;
			return result;
		}
		
		public double Modulus(double number1, double number2)
		{
			result = number1 % number2;
			return result;
		}
	}
}

Big brain

However, here’s an interesting message.

Microsoft (R) Visual C# Compiler version 4.8.4084.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

[justify]This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see Bing

Seriously, this is quite annoying to know that you don’t have the latest version of a language. I just downloaded .NET 5.0, but it didn’t change anything.

3 Likes

It’s probably just the runtime. AFAIK there is a separate development tools you need to download. The Godot mono instructions go into more detail on how to get those development tools without getting visual studio. If you just get visual studio, it comes with everything. Due to the way the C# language is structured, it’s probably very annoying to use the command line to compile projects, so no one uses it directly.

1 Like

I just updated or downloaded Visual Studio 2019 by the way. Any advice?

Encapsulation

There are access specifiers for class data :

  • public
  • private (default)
  • protected
  • internal
  • protected internal

public: Renders a member visible by other classes.
private: Hides a member from other classes, making it accessible only within the same class.
protected: Restrains a member to be accessible only by the same class and its child.
internal: Extends the scope of a member to other classes within the same “application”.
Does it mean within the same namespace?
protected internal: Restricts the scope of a member to the same class and its children only provided that they are within the same “assembly”.

Methods

To define a method:

<access> <return_type> name(parameters)
{
    code;
}

If there is nothing to return, just use void.

public int GetArea(length, width)
{
    return length * width;
}

Also, is there a naming convention for methods? What do you recommend?

To call a method, create the appropriate object if necessary and use the dot syntax.

r.GetArea(2, 7);

You can also call a method within itself, which is a technique called recursion. The website has a good example surrounding factorials. You don’t have to use the dot syntax for recursion.

To pass parameters, there are three ways : by value, reference or output. Value is the default one. Just pass a value as an argument.

int a = 5;
int b = 6;
n.GetArea(a, b);

Reference parameters are another thing. You see, value parameters work by copying the argument’s value into the parameter, which has its own memory location. Reference parameters don’t have to create one because they already refer to another one. Use the ref keyword.

public void swap(ref int x, ref int y) {...}

int a = 340;
int b = 566;

n.swap(ref a, ref b);

Same thing for output parameters, except that the keyword is out and it actually returns the parameter outside the method. So if you define the parameter x as equal to 5 inside the method, you can modify another variable outside the class.

public void getValue(out int x)
{
    y = 5;
    x = y;
}
...
int a;

n.getValue(out a);

/* a is now equal to 5 */

To be continued…

1 Like