Continuing the post...
C# Loops
for
(int
i = 0; i < length; i++)
{
}
foreach
(var
item in
collection)
{
}
do
{
}
while
(true);
while
(true)
{
}
|
Conditional constructs and equality
No
more C/C++ style comparing to non 0. Everything must be resolved to
bool in a condition statement.
string[]
myArray = { "str1",
"str2",
"str3"
};
var
result = from
el in
myArray select
el;
if
(result.ToList().Count > 0)
{//
works
}
if
(result.ToList().Count)
{//doesn't
work
}
|
Switch statement
- Unlike Java/C++ there is no fall-through (Motivation: error-free code). The code below will not compileMyEnum val = MyEnum.Value1;
switch
(val)
{
case
MyEnum.Value1:
System.Console.WriteLine("value1");
case
MyEnum.Value2:
case
MyEnum.Value3:
System.Console.WriteLine("value2");
break;
default:
break;
}
|
- Unlike C++/Java, the enumeration type must be specified
- Unlike Java (Java 7 has it already), you can switch over String, but with a decrease in performanceString switchVar = "str1";
switch
(switchVar)
{
case
"str1":
break;
case
"str2":
break;
case
"str3":
break;
default:
break;
}
|
- Fall-through can be simulated using goto keywordMyEnum val = MyEnum.Value1;
switch
(val)
{
case
MyEnum.Value1:
System.Console.WriteLine("value1");
goto
case
MyEnum.Value3;
case
MyEnum.Value2:
case
MyEnum.Value3:
System.Console.WriteLine("value2");
break;
default:
break;
}
|
Method
with different type of parameters, with parameter modifiers (ref,
out, params)
///
<summary>
///
Example of method with different type of parameters
///
</summary>
///
<param
name="p1">Passed
by reference</param>
///
<param
name="p2">Passed
by reference</param>
///
<param
name="p3">Passed
by value</param>
///
<param
name="mC">Passed
by reference</param>
///
<param
name="mS">Passed
by value</param>
///
<param
name="morePs">params.
More parameters grouped in a single array</param>
public
void
ParamExample1(out
int
p1, ref
byte
p2, String
p3, MyClass
mC, MyStruct
mS, params
Object[]
morePs)
{
//
out must be always assigned before returning from this method.
//
Compile error otherwise
p1
= 7;
//
ref assigned by the caller and optionally reassigned by the called
method.
p2
= 3;
p3
= "test_string";
Console.WriteLine(morePs.Length);
//
2
mC.var1
= 9;
mC.var2
= true;
mS.var1
= 2;
mS.var2
= 0;
}
|
Methods and parameters
Optional parameters
///
<summary>
///
Method with optional parameters
///
</summary>
///
<param
name="optStr">if
you don't set it, you cant set optObj</param>
///
<param
name="optObj">if
you want to set it, you must set optStr</param>
public
void
ParamExample2(String
optStr = "somestr",
Object
optObj = null)
{
Console.WriteLine(optStr);
Console.WriteLine(optObj);
//
Any assignment inside this scope will not affect the object
//
in outer scope unless you set some of its properties
optObj
= new
Object();
}
|
Ref parameter modifier on a reference type
///
<summary>
///
If you declare such a method, you are able to change inside such
///
method the object the outer scope reference references to... :)
///
</summary>
///
<param
name="refToRef"></param>
public
void
ParamExample3(ref
Object
refToRef)
{
refToRef
= new
MyClass();
((MyClass)refToRef).var1
= 1234;
((MyClass)refToRef).var2
= true;
}
|
Named parameters example
///
<summary>
///
Method to show named parameters example
///
</summary>
///
<param
name="param1"></param>
///
<param
name="param2"></param>
public
void
ParamExample4(MyClass
param1, MyStruct
param2)
{
Console.WriteLine(param1.var1);
//
9
Console.WriteLine(param1.var2);
//
True
Console.WriteLine(param2.var1);
//
0
Console.WriteLine(param2.var2);
//
0
}
|
The main method (and types declarations) that uses all the methods from above
public
static void
RunExample()
{
Program
prog = new
Program();
int
param1; //
Initialized to 0
byte
param2 = 4;
String
param3 = "before_assign";
MyClass
myClass = new
MyClass();
MyStruct
myStruct = new
MyStruct();
prog.ParamExample1(out
param1, ref
param2, param3, myClass, myStruct, "4235",
4);
Console.WriteLine(param1);
//
7\n
Console.WriteLine(param2);
//
3\n
Console.WriteLine(param3);
//
"before_assign"
Console.WriteLine(myClass.var1);
//
9\n
Console.WriteLine(myClass.var2);
//
True\n
Console.WriteLine(myStruct.var1);
//
0\n
Console.WriteLine(myStruct.var2);
//
0\n
prog.ParamExample2();
//
somestr,\n\n (New line in console)
prog.ParamExample2("optStr");
//
optStr,\n\n
prog.ParamExample2("optStr2",
param1); //
optStr2,\n7
//prog.paramExample2(new
DateTime()); // You can't do that
Console.WriteLine(param1);
//
7
Object
objEx3 = DateTime.Now;
prog.ParamExample3(ref
objEx3);
Console.WriteLine(objEx3);
//
1234True
//
named parameters example. All named parameters must be listed at the
//
end of the method if starting with positional parameters
//
Output is: 9\nTrue\n0\n0\n
prog.ParamExample4(param2:
myStruct, param1: myClass);
//Usefullness
of named parameters: in conjunction with optional parameters
//
Output is: somestr\nSystem.Object\n
prog.ParamExample2(optObj
: new
Object());
Console.ReadLine();
//
Waiting the user to press Enter
}
public
class
MyClass
{
public
int
var1;
public
bool
var2;
public
override
String
ToString()
{
return
var1.ToString() + var2.ToString();
}
}
public
struct
MyStruct
{
public
Int64
var1;
public
Int32
var2;
public
void
DisplayValues()
{
Console.WriteLine(var1);
Console.WriteLine(var2);
}
}
|
Method Overloading
A
method is said to be overloaded if you have multiple identically
named methods with different number of parameters or with different
parameter types. Note that only a different return type results in a
compiler error.
C# Arrays
An
array is a set of data accessed using a numerical index.
Initialization of the arrays
///
<summary>
///
Example of arrays in C#. Index starts at 0 in C#
///
</summary>
public
static
void
ArraysInitializationExample()
{
//
Just some test data
int
i1 = 8;
double
d1 = 7.7;
Object
obj1 = new
Object();
//
1.
//
Array elements are initialized to the default values
int[]
intsArray = new
int[10];
intsArray[0]
= i1;
//intsArray[10]
= 6; // This will result in an IndexOutOfRangeException
//
2. Implicitly typed local array
//
The size is determined from the number of elements and the elements
must
//
be implicitly convertible to the array type
var
stringArray = new
String[]
{ "str1",
"str2",
"str3"
};
//
3.
//
The size is constant and the elements must be implicitly convertible
//
to the array type
Object[]
objArray = new
Object[3]
{ new
int(),
d1, obj1 };
//
Output is: 0\n7.7\nSystem.Object
foreach
(Object
obj in
objArray)
{
Console.WriteLine(obj);
}
//
4.
//
The type is inferred from the best match for all given elements.
//
If none found, "No best type found for implicitly typed array"
is shown
var
forthArray = new[]
{ 4.5, 6.7, 5 };
//
5.
//
Syntax available only in a declaration (Data type specified)
Single[]
dblArray = { 5.7f, 6.6f };
//
6.
//
Initializing an one dimensional array using System.Array,
//
and spefifying the desired type. You can also create a
multi-dimensional array
Int16[]
int16Array = (Int16[])Array.CreateInstance(typeof
(Int16),
4);
int16Array[0]
= 1; int16Array[1] = 2; int16Array[2] = 3; int16Array[3] = 4;
//
Output is: 1234
for
(int
i = 0; i < int16Array.Length; i++)
{
Console.Write(int16Array.GetValue(i));
}
Console.ReadLine();
}
|
System.Array
This
is an abstract class that provides many useful methods that tend to
simplify your work with the arrays. For instance, you can: Clear(),
Copy(), IndexOf(), Reverse(), Sort() etc.
Enums in C#
An
enum is a custom data type of name/value pairs.
Standard
enum definition
public
enum
MyEnum
{
ENUMVAL1,
//
0
ENUMVAL2,
//
1
ENUMVAL3,
//
2
ENUMVAL4,
//
3
ENUMVAL5
//
4
}
|
Custom
enum order
In
an enum where the elements are not sequential, and the user doesn't
specify some of the enum values, the compiler fills the gaps
automatically.
public
enum
MyCustomOrderEnum
{
ENUMVAL1
= 9,
ENUMVAL2
= 0,
ENUMVAL3,
//
1
ENUMVAL4
= 4,
ENUMVAL5
//
5
}
|
Underlying
type of the enums
By
default, the underlying type of an enum is Int32. However, you can
specify another type from the set (byte, sbyte, short, ushort, int,
uint, long, ulong). Yes, you can't use String (A hack available).
public
enum
MyByteEnum
: byte
{
BYTEENUMVAL1
= 45,
BYTEENUMVAL2
= 37,
BYTEENUMVAL3//
= 256 This will result in a compiler error, byte 0-255
}
|
If
you want to have strings in enumeration, you can do something like
this:
public
enum
MyStringEnum
{
[StringValue("This
is value 1")]
VALUE1
= 0,
[StringValue("This
is value 2")]
VALUE2
= 1,
[StringValue("This
is value 3")]
VALUE3
= 3
}
public
class
StringValueAttribute
: System.Attribute
{
private
String
value;
public
StringValueAttribute(string
value)
{
this.value
= value;
}
public
String
Value
{
get
{ return
value; }
}
}
|
The
biggest drawback is that to access this value, you have to type a lot
of code:
MyStringEnum
mSE = MyStringEnum.VALUE1;
StringValueAttribute[]
strValueAttr =
mSE.GetType().GetField(mSE.ToString()).GetCustomAttributes(typeof(StringValueAttribute),
false)
as
StringValueAttribute[];
if
(strValueAttr.Length > 0)
{
Console.WriteLine(strValueAttr[0]);
}
If
you want to automate this code, you can make it an extension
public
static
class
StringValueAttributeExtension
{
public
static
String
GetStringAttribute(this
Enum
value)
{
var
sva =
(trainingTestProject.Program.StringValueAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(trainingTestProject.Program.StringValueAttribute),
false);
return
sva.Length > 0 ? sva[0].Value : value.ToString();
}
}
Then
you can call it by using:
Console.WriteLine(mSE.GetStringAttribute());
|
Another
approach is to declare a sealed class [1].
The
System.Enum type provides many methods to work with enums, the most
used ones being GetValues() - used to retrieve the values in the
enum, that can be cast to obtain the value in the underlying data
type, GetNames() - which returns the names of the values of the enum,
Structures (Structs)[Structs_Info]
Structures
are lightweight classes that support encapsulation, but that cannot
be used for inheritance. Data in a struct usually must be accessed
through public properties, but created as private. As with classes,
you can define custom constructors.
public
struct MyStruct2
{
private
Int64
var1;
private
Int32
var2;
public
Int32
Var2 { get
{ return
var2; } set
{ var2 = value;
} }
public
Int64
Var1 { get
{ return
var1; } set
{ var1 = value;
} }
public
void
DisplayValues()
{
Console.WriteLine(var1);
Console.WriteLine(var2);
}
}
public
static void
StructExample()
{
//
Compiler error.
//
The values of the struct must be assigned before use
/*MyStruct2
myStruct2;
myStruct2.DisplayValues();*/
//
Compiler error. For some reason, the compiler doesn't understand
//
that you've assigned the fields
/*MyStruct2
myStruct2_1;
myStruct2_1.Var1
= 10;
myStruct2_1.Var2
= 20;
myStruct2_1.DisplayValues();
// 10\n20\n*/
//
OK, compiler understands that you've assigned all the values
MyStruct
myStruct;
myStruct.var1
= 10;
myStruct.var2
= 20;
myStruct.DisplayValues();
//
10\n20\n
//
In this case default values are assigned by the constructor
MyStruct2
myStruct2_2 = new
MyStruct2();
myStruct2_2.DisplayValues();
//
0\n0\n
Console.ReadLine();
}
|
Value Types, Reference types and assignment operator
publicFONT>
static
void
ValueRefTypesExample()
{
MyStruct
myStruct1;
myStruct1.var1
= 1;
myStruct1.var2
= 5;
MyStruct
myStruct2 = new
MyStruct();
myStruct2
= myStruct1;
myStruct1.var1
= 2;
//
Because when assigning one ValueType to another,
//
a member-by-member copy operation is done, changes to one
//
instance will not be reflected to another one, they are independent
Console.WriteLine(myStruct2.var1);
//
1
Console.WriteLine(myStruct2.var2);
//
5
//
Classes are reference types. When assigning one ref type to another,
//
the reference is updated, and the object points to the newly assigned
//
Object in memory, so changes to one object will be applied to
//
another object
MyClass
myClass1, myClass2 = new
MyClass();
myClass2.var1
= 8;
myClass2.var2
= true;
myClass1
= myClass2;
myClass1.var1
= 4;
Console.WriteLine(myClass2.var1);
//
4
Console.WriteLine(myClass2.var2);
//
true
Console.ReadLine();
}
|
Value Types containing Reference Types
If
a Value Type contains a Reference Type, the copying law is the
following:
- Value Types are copied by value
- Reference type are copied by reference
So
in fact you can think of this as a shallow copy.
If
you need a deep copy, you must implement ICloneable.
References: