Language elements from C-Sharp

from Wikipedia, the free encyclopedia

This article provides an overview of some of the language elements in C # .

Conditional execution (if, else, switch)

The so-called if-Block has the consequence that the instructions between the curly brackets only come into effect if the specified condition is met, i.e. H. their worth trueis. The elseblock, which can only be linked to a ifblock, is only executed if the statements of the if block have not been executed.

if (Bedingung)
{
    Anweisungen;
}
else if (Bedingung)
{
    Anweisungen;
}
else
{
    Anweisungen;
}

The switchstatement is the representation of a case in which, depending on the value of an expression, other statements must be executed. The system always continues with the caselabel whose value switchmatches the expression in the statement. If none of the cases apply, the system defaultjumps to the optional branch.

switch (Ausdruck)
{
    case Fall_1:
        Anweisungen;
        break;
    case Fall_2:
        Anweisungen;
        break;
    default:
        Anweisungen;
        break;
}

Unlike C and C ++, the C # switchstatement has no fall-through semantics. Non-empty branches must with a break, continue, goto, throwor returnend; however, empty branches can be continued by other case branches to handle multiple cases with one set of statements:

switch (Ausdruck)
{
    case Fall_1:
    case Fall_2:
        Anweisungen;
        break;
}

Strings are also allowed as test expressions in C #:

string cmd;
//...
switch (cmd)
{
    case "run":
        Anweisungen;
        break;
    case "save":
        Anweisungen;
        break;
    default:
        Anweisungen;
        break;
}

Loops (for, do, while, foreach)

If a forloop is executed, the start expression becomes valid first. If this is completely processed, the instructions in the loop body and then the increment expression are processed repeatedly until the validity falsecondition results, i.e. i.e. becomes invalid.

for (Startausdruck; Gültigkeitsbedingung; Inkrementierungsausdruck)
{
    Anweisungen;
}

The whileloop, on the other hand, is quite primitive: it repeats the instructions as long as the condition truereturns. The condition of the whileloop is always evaluated before the statement block. If the condition is not fulfilled from the beginning, the loop body is not passed through.

while (Bedingung)
{
    Anweisungen;
}

The condition of the Do-While-Loop is always executed after the statement block. The loop is therefore run through at least once.

do
{
    Anweisungen;
} while (Bedingung);

The foreachloop is used to iterate through all members of a sequence . In the loop there is only read access to the loop variable.

foreach (Typ Variablename in Sequenz)
{
    Anweisungen;
}

The jump statements break , continue , goto , return , yield return / break

for (int i = 0; i < 10; ++i)
{
    if (i < 8)
        continue;
    Console.WriteLine("Continue wurde nicht ausgeführt.");
}

The statement continueinitiates the next iteration of a loop. (The rest of the code in the loop body is not processed). In the example, the text is only output twice.

for (int i = 0; i < 100; ++i)
{
    if (i == 5)
        break;
    Console.WriteLine(i);
}

The statement breakcauses the program to exit the next enclosing loop (or the enclosing switch ). In this example only the numbers 0, 1, 2, 3 and 4 are output.

int a = 1;
Top:
a++;
if (a <= 5)
    goto Top;
Console.WriteLine("a sollte jetzt 6 sein.");

With gotothe program jumps to the specified jump destination.

However, the use of gotoshould be avoided as far as possible, as this usually makes the source code more illegible. However, it is considered an accepted language means to leave deeply nested loops, since in these cases the code is gotomore readable than through repeated use of breakor other language means. See also spaghetti code .

Within an switchinstruction, you can jump to one of the cases using goto caseor goto default.

int result = ImQuadrat(2);

static int ImQuadrat(int x)
{
    int a;
    a = x*x;
    return a;
}

The returncurrent method is exited with and the reference or value type agreed in the header of the method is returned as a return value. Methods without a return value are marked with the keyword void.

The expression is a specialty yield return. The purpose is to generate a return sequence for a method or a property in abbreviated form. For this purpose, the compiler uses its own type, which is derived from System.Collections.Generic.IEnumerable <T> and can therefore be foreachrun through with a block. Each call to yield returnadds a new element to the sequence until the method / property is exited. If the construct is not even called, the sequence is empty:

private int[] zahlen = new int[] { 5980, 23980 };

public IEnumerable<int> ZahlenMinusEins
{
    get
    {
        foreach (int zahl in this.zahlen)
        {
            yield return zahl - 1;
        }
    }
}

public IEnumerable<double> ToDouble(IEnumerable<int> intZahlen)
{
    foreach (int zahl in intZahlen)
    {
        yield return (double)zahl;
    }
}

This means that there is no need to generate a temporary list that first stores the converted numbers and then returns them:

private int[] zahlen = new int[] { 5980, 23980 };

// bspw. aus einer Eigenschaft heraus
public IEnumerable<int> ZahlenMinusEins
{
    get
    {
        List<int> rückgabe = new List<int>();

        foreach (int zahl in this.zahlen)
        {
            rückgabe.Add(zahl 1);
        }

        return rückgabe;
    }
}

Each element that is returned must be implicitly convertible to the type of the return sequence elements.

The instruction yield breakcan be used to cancel the process :

// bspw. aus einer Methode heraus
public IEnumerable<double> ToDouble(IEnumerable<int> intZahlen)
{
    int i = 0;
    foreach (int zahl in intZahlen)
    {
        if (i++ == 3)
        {
            // nach 3 Durchläufen beenden
            yield break;
        }

        yield return (double)zahl;
    }
}

The instructions returnand yield returncannot be used together.

When using it, it must be noted that the returned sequence executes the underlying logic with a delay , which means that the first run of the same sequence can return different values ​​than the second time:

// interner Zähler
private int i = 0;

public IEnumerable<DateTime> GetNow() {
    // aktuelle Uhrzeit (einziges Element)
    yield return DateTime.Now;
}

public IEnumerable<int> GetZahl() {
    // internen Zähler um 1 erhöhen (einziges Element)
    yield return this.i++;
}

public void TestZahl() {
    IEnumerable<int> sequenz = this.GetZahl();

    foreach (int zahl in sequenz)
        Console.WriteLine(zahl);  // 0

    foreach (int zahl in sequenz)
        Console.WriteLine(zahl);  // 1

    foreach (int zahl in sequenz)
        Console.WriteLine(zahl);  // 2
}

public void TestZeit() {
    IEnumerable<DateTime> sequenz = this.GetNow();

    // [1] einziges Element mit der aktuellen Uhrzeit
    foreach (DateTime now in sequenz)
        Console.WriteLine(now);

    // 2 Sekunden warten
    Thread.Sleep(2000);

    // [2] einziges Element wie der Wert aus [1]
    //   + ca. 2 Sekunden
    foreach (DateTime now in sequenz)
        Console.WriteLine(now);
}

The using statement

The usingstatement defines a scope at the end of which the memory of objects that are no longer required is automatically released. In the case of limited resources such as file handlers, the usingstatement ensures that these are always cleaned up exceptionally safe.

using (Font myFont = new Font("Arial", 10.0f))
{
    g.DrawString("Hallo Welt!", myFont, Brushes.Black);
}

An Fontobject is created here, which is automatically Disposereleased again at the end of the block by calling its method. This happens even if an exception is thrown in the block. The advantage lies in the simplified notation, because internally this results in the following construct, which would otherwise have to be formulated manually:

Font myFont = new Font("Arial", 10.0f);

try
{
    g.DrawString("Hallo Welt!", myFont, Brushes.Black);
}
finally
{
    if (myFont != null)
    {
        myFont.Dispose();
    }
}

Classes must System.IDisposableimplement the interface in order for the usingstatement to be used in this way.

Objects and classes

When one speaks of an object, it is usually a real object or an object of everyday life in everyday language. For example, this can be an animal, a vehicle, an account or the like.

Each object can be described by different attributes and can assume and trigger different states.

Transferred to object-oriented programming and C #, an object is an instance (see keyword new) of a class. A class can be seen as a blueprint or scaffolding of an object.

A class has properties (variables), methods (which represent activities) and events which are the result of states or which trigger them.

class
Properties)
Method (noun)
Event (s)

Example of a blueprint for a car:

class Auto
{
    // Konstruktor, dient zur Erzeugung
    public Auto(string name, System.Drawing.Color farbe)
    {
        this.GeschwindigkeitKMH = 0; this.Name = name; this.Farbe = farbe;
    }

    // Eigenschaften:
    public double GeschwindigkeitKMH { get; private set; }
    public string Name { get; private set; }
    public System.Drawing.Color Farbe { get; private set; }
    private bool motorLaeuft = false;

    //Methoden:
    public void Beschleunigung(double AenderungKMH)
    {
        this.GeschwindigkeitKMH += AenderungKMH;
        GeschwindigkeitGeaendert(this.GeschwindigkeitKMH);
    }
    public void VollBremsung()
    {
        this.GeschwindigkeitKMH = 0d;
        GeschwindigkeitGeaendert(this.GeschwindigkeitKMH);
    }
    public void MotorStarten()
    {
        if (!this.motorLaeuft)
        {
            this.motorLaeuft = true;
            MotorEreignis(this.motorLaeuft);
        }
    }
    public void AutoStoppen()
    {
        if (this.GeschwindigkeitKMH != 0d) { VollBremsung(); }
        if (this.motorLaeuft)
        {
            this.motorLaeuft = false;
            MotorEreignis(this.motorLaeuft);
        }
    }
    public void Umlackieren(System.Drawing.Color neueFarbe) { this.Farbe = neueFarbe; }

    //Ereignisse
    public event MotorDelegat MotorEreignis;
    public event GeschwindigkeitDelegat GeschwindigkeitGeaendert;

    public delegate void MotorDelegat(bool laeuft);
    public delegate void GeschwindigkeitDelegat(double geschwindigkeit);
}

The class called "object"

The class object(an alias for System.Object) is a reference type from which each class is derived. Any object type can be converted into by implicit type conversionobject .

.NET (and thus also C #) differentiates between value types and reference types. However, value types are also objectderived from (via the ValueType intermediate step) . Therefore (by means of a process called boxing / unboxing) a variable of the type can also be assigned objectto a value type, e.g. B. intrefer.

Structures ( struct )

Structures are already known as language resources from languages ​​such as C ++ or Delphi (records). They are primarily used to create your own complex data structures or your own data types. For example, a spatial coordinate consisting of an X, a Y and a Z position can be mapped using a coordinate data structure that is composed of three floating point numbers with single or double precision.

public struct Koordinate
{
    public double X { get; }
    public double Y { get; }
    public double Z { get; }

    public Koordinate(double x, double y, double z)
    {
         X = x;
         Y = y;
         Z = z;
    }

    public override string ToString()
    {
        return $"x: {X}, y: {Y}, z: {Z}";
    }
}

C # understands a structstructure defined by the keyword as a value type. Structures in C # can also have methods, properties, constructors, and other elements of classes; but they cannot be inherited.

The difference between a structure instance and an object lies in its representation in memory. Since no additional memory references are required, as is required for an object (reference type), structure instances can be used in a much more resource-saving manner. For example, all primitive data types in C # are struct-based.

Lists (enumerations)

Lists are used for the automatic numbering of the elements contained in the list. The syntax for defining lists uses the keyword enum(abbreviation for enumeration).

The enumeration type used in C # is similar to that in C, with the exception that an optional integer data type can be specified for numbering the elements. If no data type is specified, it is intused.

public enum Wochentag
{
    Montag = 1, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag
}

The element numbering in C # starts at 0. It is also possible, as in C, to assign each element - or only the first element - its own start value (as in the example above). The initial values ​​can be repeated. The counting then starts again with the defined start value and element.

In C # it is also possible to address a certain element of an enumeration via its ordinal number. However, an explicit type conversion is necessary for this.

Wochentag Tag = Wochentag.Mittwoch;
System.Console.WriteLine(Tag); // Ausgabe: Mittwoch
System.Console.WriteLine((int)Tag); // Ausgabe: 3
System.Console.WriteLine((Wochentag)3); // Ausgabe: Mittwoch

Flags

In addition to the described variant of the enum enumeration type, there is also a special variant of enumeration. Flags define enumerations at bit level and are defined by the meta information [flags] in front of the enum declaration. Flag elements can be linked at the bit level so that several elements can be combined into a new one. For this purpose, the elements to be combined must be assigned powers of two as values ​​so that a combination is possible.

[Flags]
public enum AccessMode
{
    None = 0,
    Read = 1,
    Write = 2,
    // Erhält den Wert 3 (Kombination aus 1 und 2)
    ReadAndWrite = Read | Write
}

Access modifiers

Access modifiers regulate access to classes and their members (methods, properties, variables, fields and events) in C #. The following table lists the access modifiers supported by C # and describes their effect and the visibility context.

Surname effect
abstract Abstract members are declared but not implemented. The implementation is mandatory in the derived class. An instance cannot be created from an abstract class.
internal internal restricts access to classes and their members to an assembly.

This is not a restriction to namespaces, but to the assembly (i.e. to the respective executable file or the class library). Under Java this corresponds to the restriction to one package.

new Calling the constructor is not meant here. The newmodifier hides a member of the base class. The class member has the same signature but different functionality and is not related to the hidden member of the base class. When called, all base class members with the same name are hidden. Without this modifier, the compiler issues a warning.
override The modifier overrideoverwrites the abstract or virtual implementation of a method or property, or an indexer or event of the base class. override-Members are automatic virtual. In the inheritance hierarchy it is a further implementation.
private Restricts access to a class and its members. A privateclass declared with can only be instantiated within the class itself (for example, a public constructor of a class, or the static function Main , can call a private constructor that cannot be called from outside).

It is privateoften used to implement the singleton pattern (e.g. when using a class as a factory; see factory method ) or to influence or prohibit inheritance (see also keyword sealed). Note: A derived class can basenot access private members of the base class (see keyword ) either. If such access is to be possible, the access modifier protectedmust be used.

protected The modifier protectedonly allows access in its own inheritance hierarchy, i.e. only for the declaring class and all derived classes.
protected internal The modifier protected internalis a combination of the modifiers protectedand internal. The method is visible to the declaring and to all derived classes as well as to all classes within the assembly. A visibility only for derived classes within the assembly does not exist in C #. In this case the modifier internalmust be used.
public publicClasses or class members marked as (e.g. methods or properties) can be accessed without restriction. They are therefore also referred to as "public".
sealed sealedcan be applied to classes, instance methods and properties. Cannot derive from sealed classes. A sealed method overrides a base class virtual method. The sealed method remains visible to inheriting classes, but it can no longer be overridden. This is the last implementation in the inheritance hierarchy.
static static-Members are class-specific, but not instance-specific. No instance can be created from static classes. Static members of a class are called with the name of the class, not the name of the object instance (example:) Math.Sqrt(). A separate copy of all instance fields is created for each instance of a class; there is only one copy for static fields.
virtual The virtualmodifier indicates that this is the first implementation in the inheritance hierarchy. Virtual members can be overrideoverridden with the modifier in a derived class . If virtuala member is missing , it is the only implementation.

Hints:

  • By default, class members (methods, properties, etc.) to which no access modifier has been assigned are automatically privatedeclared. Classes themselves, however, automatically have the modifier internaland are only visible in the current assembly.
  • The modifiers can be combined with one another except for protectedand internal. protected internalplays a role in connection with the inheritance of components. The visibility of the base class is inherited from the derived class.
  • To implement an interface member, a class member must either be publicdeclared as or the interface member must be implemented explicitly.

Data types, operators, properties and constants

C # knows two kinds of data types : value types and reference types . Reference types must not be equated with pointers , as they may be . a. are known from the C ++ language. These are also supported by C #, but only in "secure mode" (English. Unsafe mode ).

Value types contain the data directly, whereas reference types, in contrast, only represent references to the actual data, or better, objects . When reading and writing value types, on the other hand, the data is stored in an instance of the respective envelope class ( wrapper ) or loaded from it via an automatic mechanism called autoboxing .

A value or a reference can be assigned during the declaration or later, unless a constant has been declared. The declaration is made by specifying a data type followed by a variable name:

// Datentyp Variable;
int i;
System.Collections.IList liste;

Several variables of the same type can be declared at the same time:

// Datentyp Variable1, Variable2, ...;
int i, j, k;
System.Collections.IList liste1, liste2;

It is also possible to assign a value or a reference to the variable when declaring it ( initial value ):

// Datentyp Variable=Wert/Referenz;
int i = 5;
int j = 2, k = 3;
System.Collections.IList liste = new System.Collections.ArrayList();

Multiple assignment of a value to different variables is also possible:

int i, j, k;
i = j = k = 123;

The declaration of fields (arrays) is a special case of assignment. More on this in the corresponding section.

Data types and memory requirements

Data type bit suffix Sign Alias ​​for (struct type)
bool 8th - System.Boolean
byte 8th N System.Byte
char 16 - System.Char
decimal 128 m, M - System.Decimal
double 64 d, D - System.Double
float 32 f, F - System.Single
int 32 J System.Int32
long 64 l, L J System.Int64
sbyte 8th J System.SByte
short 16 J System.Int16
uint 32 u, U N System.UInt32
ulong 64 ul, UL N System.UInt64
ushort 16 N System.UInt16

In C #, data types are not elementary, but object-based . Each of the data types listed in the table represents an alias to a class in the System namespace . For example, the data type boolis System.Booleanmapped by the class . The object-based nature makes it possible to apply methods to data types:

1234.ToString();
int i = 17;
i.ToString();

Comparable to C ++, and unlike Java , there are signed and unsigned data types under C #. These are identified by prefixing the letter s (for signed , English for signed ) and by prefixing the letter u (for unsigned , English for unsigned ) ( sbyte, uint, ulong, ushort,with the exception of short). The floating point data types ( float, double, decimal) can have double precision as well as single precision and have varying memory requirements. This changes the accuracy, which is reflected in the number of possible decimal places.

Constants (keyword const )

No new content can be assigned to an const“object” declared with after declaration and initialization. This turns the "object" into a constant .

The compiler must be able to determine that the value assigned to a constant cannot be changed. It is also possible to define a constant of a reference type, but this can only be assigned zero .

The reason for this is that the compiler replaces all uses of constants at the time of compilation. Structures cannot be constant because they could use a random number generator in a constructor .

Incorrect assignments of a constant are criticized by the C-Sharp compiler with compile error CS0133.

using System;
using System.IO;

public class ConstBeispiel
{
    public static void Main()
    {
        //Es wird eine Konstante für die Länge eines Arrays angelegt
        const int arrayLength = 1024;
        //Es wird eine Konstante festgelegt, welche die Zugriffsart auf eine Datei beschreibt (Enum)
        const FileMode fm = FileMode.Open;
        //Es wird eine Konstante festgelegt, welche den Namen der zu öffnenden Datei festlegt
        const string fileName = "beispiel.txt";

        //Buffer erstellen
        byte[] readBuffer = new byte[arrayLength];
        //Stream zur Datei öffnen
        FileStream fs = new FileStream(fileName,fm);
        //Daten Lesen
        fs.Read(readBuffer,0,readBuffer.Length);
        //Stream schließen
        fs.Close();
        //Daten ggf. bearbeiten.
        //...

        // FEHLER: IList wird ein Referenzdatentyp zugewiesen, nicht konstant
        const IList liste = new ArrayList();

        // FEHLER: const + struct
        const TimeSpan zeitSpanne = new TimeSpan(10);
    }
}

There are also constants in other languages ​​(e.g. C ++, Java). In Java constants are identified by the keyword final, in Fortran by PARAMETER.

Operators

Operators perform various operations on values, creating a new value in the process. Depending on the number of operands, a distinction is made between unary, binary and ternary operators. The order of the evaluation is determined by the priority and associativity and can be changed using parentheses.

Operator precedence operator description
1. Primary operators xy Member access
f (x) Calling methods and delegates
a [x] Array and indexer access
x ++ Post increment
x-- Post decrement
new T (...) Object and delegate creation
new T (...) {...} Object creation with initializers
new {...} Anonymous object initializer
new T [...] Array creation
typeof (T) Get the System.Type object for T
checked (x) Evaluating expressions in a checked context
unchecked (x) Evaluating expressions in an unchecked context
default (T) Get the default value of type T
delegate {} Anonymous method
2. Unary operators + x identity
-x negation
! x Logical negation
~ x Bitwise negation
++ x Pre-increment
--x Pre-decrement
(T) x Explicitly converting x to type T
3. Multiplicative operators * multiplication
/ division
% Modulo
4. Additive operators x + y Addition, string concatenation, delegate combination
x - y Subtraction, delegate removal
5. Shift operators x << y Left shift
x >> y Right shift
6. Relational operators and type operators x <y Less than
x> y Greater than
x <= y Smaller or equal
x> = y Greater or equal
x is T Returns true if x is of type T; false otherwise.
x as T Returns x typed as T, or NULL if x is not of type T.
7. Equality operators x == y Equal
x! = y Unequal
8. Logical, conditional and NULL operators x & y Integer bitwise AND, Boolean logical AND
x ^ y Integer bitwise XOR, Boolean logical XOR
x | y Integer bitwise OR, Boolean logical OR
x && y Evaluates y only if x is true.
x || y Evaluates y only if x is false.
x ?? y Returns y if x is NULL; x otherwise
x? Y Z Evaluated as y if x is true and evaluated as z if x is false.
9. Assignment operators and anonymous operators = allocation
x op = y Association assignment. Corresponds to x = x op y, where op is the operator +, -, *, /,%, &, | << or >> is.
(T x) => y Anonymous method (lambda expression)

Operators overloaded

C # has the ability to implement operators for user-defined data types. A self-written class or structure is considered a user-defined data type. The implementation is done with public static methods. Instead of a name, they have the keyword operatorfollowed by the operator character which is to be overloaded. An implicit or explicit type conversion is done using implicit operatoror explicit operatoras method names. Depending on the type of operand, the compiler replaces the source code in a call to the corresponding method:

struct SpecialDouble
{
    public SpecialDouble(double argument)
    {
        _value = argument;
    }

    // SpecialDouble lhs = 9d, rhs = 0.5, result;
    // result = lhs ^ rhs; // Compiler ersetzt Operator ^ mit dieser Methode
    public static SpecialDouble operator ^(SpecialDouble lhs, SpecialDouble rhs)
    {
        return Math.Pow(lhs._value, rhs._value);
    }

    // SpecialDouble lhs = 9d; // 9d wird mit 'new SpecialDouble(9d)' ersetzt
    public static implicit operator SpecialDouble(double argument)
    {
        return new SpecialDouble(argument);
    }
    public static implicit operator double(SpecialDouble argument)
    {
        return argument._value;
    }

    // explizite Typkonvertierung:
    // Nachkommastellen gehen verloren, Exception kann auftreten
    public static explicit operator int(SpecialDouble argument)
    {
        return (int)argument._value;
    }

    double _value;
}

Restrictions:

  • At least one parameter of the method for the overload must be of the type for which the operator is overloaded.
  • Relational operators can only be overloaded in pairs.
  • No new symbols can be used. Only the operator symbols that are defined in the language are allowed. See: Operators table
  • The operators =, ., ?:, ->, new, is, sizeof, typeofand =>can not be overcharged.
  • The priority of a user-defined operator cannot be changed. Priority and orientation are based on the operator's symbol.
  • The number of operands is also linked to the symbol of the operator.
  • Operators that are not reserved cannot be implemented.

Indexers can be used []to simulate the operator . The operators and cannot be overloaded directly. They are evaluated using the special overloadable operators and . &&||truefalse

The operation x && yis T.false(x) ? x : T.&(x, y)evaluated as, where there is T.false(x)a call to the Toperator declared in falseand T.&(x, y)a call to the selected operator &.

The operation x || yis T.true(x) ? x : T.|(x, y)evaluated as, where there is T.true(x)a call to the Toperator declared in trueand T.|(x, y)a call to the selected operator |.

Properties ( get , set, and value keywords )

A property ( property ) is a view of a public variable of a class. The variable itself is blocked from external access by an access modifier such as privateor protected(for variables that are to be overwritten in derived classes) and made accessible via a property. The property can then be used to determine whether read or write access to the referenced variable is permitted. Both options can also be combined with one another.

A property is created by assigning a data type (which must correspond to the data type of the variable) to a property name and has a structure similar to the syntax of a method. The property can be addressed like a variable and an access modifier can also be assigned to it. A property itself does not contain any data, but maps it to the referenced variable (comparable to a pointer).

In C #, the keyword is used to query a property getand the keyword is used to set a value set. From the outside, the property appears like a variable and access can take place accordingly (cf. VisualBasic).

The Java programming language pursues the same goal with the set and get methods (bean pattern, introspection) - all accesses never take place directly via a variable, but via the corresponding method.

Example of a property definition Wohnortfor a private variable ( _wohnort):

public class EigenschaftBeispiel
{
    private string _wohnort;
    public string Wohnort
    {
        get
        {
            return _wohnort;
        }
        set
        {
            _wohnort = "12345 " + value;
        }
    }
}

By "omitting" the keyword setor the keyword, it getcan be controlled whether the property can only be read or only written. The keyword valueis a placeholder for the value assigned to the property that is to be set. It can only be used in connection with the keyword setin the corresponding block (and corresponds roughly to a temporary local variable).

Example of access to the property defined above Wohnort:

EigenschaftBeispiel instanz = new EigenschaftBeispiel();
instanz.Wohnort = "Musterstadt";
Console.WriteLine(instanz.Wohnort);
// Ausgabe: ''12345 Musterstadt''

If the getblock were omitted in the definition of the property 'Residence' above , the read access would lead to an access error (in the example in the line in which the output occurs).

In addition to the simple setting or reading of a property, operations can also be carried out in the setblock or getblock, for example the exponentiation of a settransferred value ( valuetimes the exponent) before it is assigned to the variable. The same goes for the keyword get. Theoretically, access for the user of a class can therefore produce completely unexpected results. Therefore, all operations that make changes to a value should be mapped using normal methods. Of course, value tests are excluded set.

The example concatenates the value transferred to the property (here: model city) to the character string “12345”. This action is syntactically and semantically correct, it should nevertheless be carried out in a method.

As of C # 3.0, it is possible to implement properties automatically. This is a shortened form of notation for properties that involve access to a variable that has been declared as a field within the class or structure.

Example based on the structure Point:

struct Point
{
    public double X
    {
        get { return this.x; }
        set { this.x = value; }
    }

    public double Y
    {
        get { return this.y; }
        set { this.y = value;}
    }

    private double x, y;
}

The same example with the help of automatically implemented properties:

struct Point
{
    public double X { get; set; }
    public double Y { get; set; }
}

With the help of the object initializer (from .NET 3.5) a constructor is superfluous:

Point p = new Point { X = 1.2, Y = -3.75 }; // Objektinitialisierer
Console.WriteLine(p.X); // Ausgabe: 1,2
Console.WriteLine(p.Y); // Ausgabe: −3,75

Indexer

The indexer is the standard property of an object. The call is made like an array with square brackets.

Example: Access to the 32 bits of a intvariable using an indexer.

using System;

namespace DemoIndexers
{
    class Program
    {
        struct IntBits
        {
            public bool this[int index]
            {
                get { return (bits & (1 << index)) != 0; }
                set
                {
                   if (value)
                   {
                     bits |= (1 << index);
                   }
                   else
                   {
                     bits &= ~(1 << index);
                   }
                }
            }

            private int bits;
        }

        static void Main(string[] args)
        {
            IntBits bits = new IntBits();
            Console.WriteLine(bits[6]); // False
            bits[2] = true;
            Console.WriteLine(bits[2]); // True
            bits[2] = false;
            Console.WriteLine(bits[2]); // False
        }
    }
}

As with properties, access can be restricted to read only or write only by omitting the get accessor or set accessor.

Differences to arrays, methods and properties:

  • Indexers must have at least one parameter.
  • In contrast to arrays, non-integer values ​​can also be used for access.

public double this[string name]{} // ist erlaubt

  • Indexers can become overloaded.
  • Static indexers are not allowed, as well voidas as return values.
  • refand outmust not be used with indexers.

Representation of special characters or character strings ("escape")

Seq. description Hexadecimal
\ Introduction of alternative interpretation 0x001B
\' Single quotation mark 0x0007
\" Double quotation mark 0x0022
\\ Backslash 0x005C
\0 zero 0x0000
\a Tone (engl. A lert ) 0x0007
\b Return step (engl. B ackspace ) 0x0008
\f Feed (engl. F orm feed ) 0x000C
\n Line break (engl. N ew line ) 0x000A
\r Carriage return (engl. Carriage r eturn ) 0x000D
\t Tab characters, horizontal 0x0009
\v Tab characters, vertical 0x000B
\x Hexadecimal string for a single Unicode character
\u String for Unicode characters in character literals.
\U String for Unicode characters in character string literals.

A character following the character “\” ( backslash ) is interpreted differently than usual. These are mostly non-displayable characters. If the backslash is to be displayed itself, it must be entered twice ("\\").

Hexadecimal character string as a placeholder for a single Unicode character: The character is formed from the control character \ x followed by the hexadecimal value of the character.

Character string for Unicode characters in character literals: The character is formed from the control character \ u followed by the hexadecimal value of the character, e.g. B. "\ u20ac" for "€" The value must be between U + 0000 and U + FFFF.

Character string for Unicode characters in character string literals: The character is formed from the control character \ U followed by the hexadecimal value of the character. The value must be between U + 10000 and U + 10FFFF. Note: Unicode characters in the value range between U + 10000 and U + 10FFFF are only permitted for character string literals and are encoded or interpreted as two Unicode "replacement characters" (see also UTF-16 ).

Inheritance

Interfaces

Multiple inheritance is only in C # in the form of interfaces (engl. Interfaces ) supported.

In C #, interfaces are used to define methods, their parameters, their return values ​​and possible exceptions.

At this point an application example for multiple inheritance:

public class MyInt : IComparable, IDisposable
{
    // Implementierung
}

Interfaces in C # are similar to the interfaces in the Java programming language. In contrast to Java, interfaces in C # must not contain any constants or agree on any access modifiers when defining a method.

public interface A
{
    void MethodeA();
}
public interface B
{
    void MethodeA();
    void MethodeB();
}
public class Klasse : A, B
{
    void A.MethodeA() {Console.WriteLine("A.A");} // MethodeA aus Schnittstelle A
    void B.MethodeA() {Console.WriteLine("A.B");} // MethodeA aus Schnittstelle B
    public void MethodeA() {Console.WriteLine("A.C");} //MethodeA für Klasse
    public void MethodeB() {Console.WriteLine("B.B");} // MethodeB aus Schnittstelle B
}

A class that includes one or more interfaces must implement every (virtual) method defined in the interface. If several interfaces are integrated that have methods with the same name and the same structure (i.e. the same parameter types, return values, etc.), the respective method must be identified in the implementing class by prefixing the name of the interface. The respective function is only called if the pointer to the object is of the corresponding type:

public static void Main()
{
    Klasse k = new Klasse();
    (k as A).MethodeA();
    (k as B).MethodeA();
    k.MethodeA();
    Console.ReadLine();
}
output
A.A
A.B
A.C

Interfaces without a method definition are also possible. They then serve as a so-called marker interface (Engl. Marker interface ). The use of marker interfaces should be avoided in favor of attributes. However, interfaces cannot define static methods.

The integration of an interface is done in the same way as inheriting a class. Interfaces are named by convention with a leading "I" (for interface).

Inheritance of interfaces

Effect of the access modifiers when inheriting with an interface
  no modifier new
code
interface IMessage {
   string Message { get; }
}

public class MyClass : IMessage {
   public string Message {
      get { return "MyClass"; }
   }
}

public class MyDerivedClass : MyClass {
   public string Message {
      get { return "MyDerivedClass"; }
   }
}
interface IMessage {
   string Message { get; }
}

public class MyClass : IMessage {
   public string Message {
      get { return "MyClass"; }
   }
}

public class MyDerivedClass : MyClass {
   public new string Message {
      get { return "MyDerivedClass"; }
   }
}
Test
(use)
MyDerivedClass mdc = new MyDerivedClass();
MyClass mc = mdc as MyClass;
IMessage m = mdc as IMessage;

Assert.AreEqual(mdc.Message, "MyDerivedClass");
Assert.AreEqual(mc.Message, "MyClass");
Assert.AreEqual(m.Message, "MyClass");
MyDerivedClass mdc = new MyDerivedClass();
MyClass mc = mdc as MyClass;
IMessage m = mdc as IMessage;

Assert.AreEqual(mdc.Message, "MyDerivedClass");
Assert.AreEqual(mc.Message, "MyClass");
Assert.AreEqual(m.Message, "MyDerivedClass");
  virtual & override abstract & override
code
interface IMessage {
   string Message { get; }
}

public class MyClass : IMessage {
   public virtual string Message {
      get { return "MyClass"; }
   }
}

public class MyDerivedClass : MyClass {
   public override string Message {
      get { return "MyDerivedClass"; }
   }
}
interface IMessage {
   string Message { get; }
}

public class MyClass : IMessage {
   public abstract string Message {
      get;
   }
}

public class MyDerivedClass : MyClass {
   public override string Message {
      get { return "MyDerivedClass"; }
   }
}
Test
(use)
MyDerivedClass mdc = new MyDerivedClass();
MyClass mc = mdc as MyClass;
IMessage m = mdc as IMessage;

Assert.AreEqual(mdc.Message, "MyDerivedClass");
Assert.AreEqual(mc.Message, "MyDerivedClass");
Assert.AreEqual(m.Message, "MyDerivedClass");
MyDerivedClass mdc = new MyDerivedClass();
MyClass mc = mdc as MyClass;
IMessage m = mdc as IMessage;

Assert.AreEqual(mdc.Message, "MyDerivedClass");
Assert.AreEqual(mc.Message, "MyDerivedClass");
Assert.AreEqual(m.Message, "MyDerivedClass");

Overwriting a method by a derived class can be sealedprevented with:

interface IMessage {
   string Message { get; }
}

public class MyClass : IMessage {
   public virtual void OnMessage() {
      // kann von abgeleiteter Klasse implementiert werden
   }

   // kann nicht überschrieben werden
   public sealed string Message {
      get {
         OnMessage();
         return "MyClass";
      }
   }
}

An interface can also be implemented by a base class:

interface IMessage {
   string Message { get; }
}

public class Messenger {
   public string Message {
      get { return "Messenger"; }
   }
}

public class MyClass : Messenger, IMessage {
   // interface bereits implementiert
}

The base keyword

The keyword is used in the context of inheritance . Put simply, the basethis class is what is for the current class. Java, however, provides the keyword for this super.

Here is an example showing how to use base:

public class Example : Basisklasse
{
    private int myMember;
    public Example() : base(3)
    {
        myMember = 2;
    }
}

In this example, usage has been demonstrated using the base class constructor only. As described in the introduction, base can also be used to access the members of the base class. Use is equivalent to using this on the current class.

Sealed classes

Sealed classes are classes from which no derivation is possible and which consequently cannot be used as base classes. The best-known representative of this type of class is the class Stringfrom the namespace System. The modifier sealedmarks classes as sealed. However, it is possible to extend sealed classes with extension methods .

Static classes

Analogous to Visual Basic .NET modules, classes can be defined in C # that consist exclusively of static elements:

static class MeineStatischeKlasse
{
    public static int StatischeEigenschaft
    {
        get { return 5979; }
    }

    public static void StatischeMethode()
    {
    }

    /* Dies würde der Compiler als Fehler ansehen, da diese Methode nicht statisch ist

    public void NichtStatischeMethode()
    {
    }
    */
}

Extension methods

As of version 3.0, data types can be extended . A static class is defined for this. Extension methods ( Engl. Extensions) each include a first parameter associated with the keyword this begins, followed by the usual definition of the parameter:

using System;

namespace MeinNamespace
{
    public static class ExtensionKlasse
    {
        public static int MalDrei(this int zahl)
        {
            return zahl * 3;
        }
    }

    public static class Programm
    {
        public static void Main()
        {
            // 5979
            Console.WriteLine(1993.MalDrei());
        }
    }
}

If the class ExtensionKlasse is visible to another class, all numbers of the type int are now extended with the method MalDrei without actually changing the type int . The compiler does nothing else internally than calling the method MalDrei of the class ExtensionKlasse and passing the value 1993 as the first parameter.

Methods

Anonymous methods

Anonymous methods may be used. a. used to store code for an event without having to define a method with a unique name in a class. The keyword delegate is used in place of the method name :

Button btn = new Button()
{
    Name = "MeinButton",
    Text = "Klick mich!"
};

btn.Click += delegate(object sender, EventArgs e)
{
    Button button = (Button)sender;

    MessageBox.Show("Der Button '" + button.Name + "' wurde angeklickt!");
};

Lambda expressions

From version 3.0 there is the possibility to define anonymous methods in a shorter form. This is done with the Lambda operator =>(pronounced: "changes to" ). The input parameters are specified on the left-hand side of the lambda operator, the statement block or an expression is on the right-hand side. If it is an instruction block, one speaks of an instruction lambda . On the other hand, an expression lambda , such as x => x * x, is a delegate whose only statement returnis a. The type of input parameters can be omitted if the compiler can infer them. Lambda expressions can refer to outer variables that are in the scope of the enclosing method or of the type in which the lambda expression was defined.

Button btn = new Button()
{
    Name = "MeinButton",
    Text = "Klick mich!"
};

// 'sender' wird implizit als System.Object betrachtet
//
// 'e' wird implizit als System.EventArgs betrachtet
btn.Click += (sender, e) => {
    Button button = (Button)sender;

    MessageBox.Show("Der Button '" + button.Name + "' wurde angeklickt!");
};

LINQ

LINQ defines three things:

  1. A syntax for query expressions that is heavily based on SQL ,
  2. Translation rules,
  3. Names (hardly any more) for methods that are used in translation results.

The functionality is implemented by so-called LINQ providers who make the methods defined by name available. One of them is, for example, LINQ-to-Objects.

// nimm die Liste der Mitarbeiter und
// schreibe jedes Element nach 'm'
var liste = from m in this.MitarbeiterListe

// lies das Property 'Nachname' und nimm nur die
// Elemente, die gleich 'Mustermann' sind
where m.Nachname == "Mustermann"

// sortiere zuerst ABsteigend nach dem Property 'Vorname'
// dann AUFsteigend nach dem Property 'MitarbeiterNummer'
orderby m.Vorname descending, m.MitarbeiterNummer

// wähle nun zum Schluss als Element für die Liste namens 'liste'
// den Wert aus dem Property 'Vorname' jedes Elements aus
select m.Vorname;

For example, in MySQL the above expression could look like this:

SELECT m.Vorname FROM MitarbeiterListe AS m
WHERE m.Nachname = "Mustermann"
ORDER BY m.Vorname DESC, m.MitarbeiterNummer ASC

Typecasts

In C #, each variable is assigned a data type. Sometimes it is necessary to convert types of variables into one another. There are cast operations for this purpose. There are implicit and explicit type conversions.

An implicit type conversion does not appear in the source code. It is automatically inserted into the generated machine code by the compiler . The prerequisite for this is that there is an implicit type conversion operation between the source and target type.

Two constructs are provided in C # for explicit type conversions:

(Zieldatentyp) Variable_des_Ursprungsdatentyps

Variable_des_Ursprungsdatentyps as Zieldatentyp

While the former conversion throws an exception in the case of an invalid type conversion, the latter is only possible if the target data type is a reference data type . If the type conversion is invalid, the null pointer is assigned to the target .

using System.Collections;
public class CastBeispiel
{
    public static void Main()
    {
        long aLong = long.MaxValue;
        //Typumwandlung nach int, aInt hat danach den Wert −1,
        //nicht in einem unchecked{}-Block eingeschlossen, wird jedoch eine Ausnahme geworfen.
        unchecked {
            int aInt = (int) aLong;
        }
        //Umwandlung nach object
        object aObject = aInt as object;
        //ungültige Typumwandlung, liste2 erhält den Wert null
        IList liste2 = aObject as IList;

        //ungültige Typumwandlung, löst zur Laufzeit eine InvalidCastException aus,
        //da das Object nicht vom Typ Liste ist.
        IList liste = (IList)aObject;
        //Löst den Kompilierfehler CS0077 aus, da int kein Referenztyp ist
        int aInt2 = aLong as int;
    }
}

The uncheckedinstruction or instruction block is used to ignore the overflow of an integer. On the checkedother hand, an OverflowException is triggered in the event of an overflow.

The type conversions also include what is known as "boxing" . It denotes the conversion between value and reference types. As in the case of explicit conversion, the target type is written in brackets in front of the type to be converted. If this is done implicitly, it is called "autoboxing" .

Custom casts

C # allows the definition of custom casts. These can be marked as explicit or implicit. Implicit custom casts may occur. a. in overload resolution, while explicit ones are used when the explicit cast syntax mentioned above is used.

See also: Operators overloaded , Explicit typecast

Parametric Polymorphism (Generics)

When defining interfaces, classes, structures, delegate types and methods, type parameters can be specified and, if necessary, given restrictions. If type parameters are given, you get generic interfaces, classes, and so on. When using such generics later, the type parameters are filled with concrete type arguments.

Definition-side covariance and contravariance

As of C # 4, it is possible to give type parameters of interfaces and delegate types a variance annotation. This influences the subtype relation. Suppose the generic type is named Kand has a single type parameter T. The following are possible:

  • Covariance : K<out T>; If is Asubtype of B, then is K<A>subtype of K<B>,
  • Contravariance : K<in T>; If is Asubtype of B, then is K<B>subtype of K<A>,
  • Invariance : K<T>; There is no subtype relationship between instantiations of Kwith different type arguments T.

(Each type parameter can be given a variance annotation independently of others.)

In addition to the continuation of subtype relationships from type arguments to instances of generics, the variance annotation also influences the positions at which a type parameter may be used. For example, the use of covariant type parameters as types of method arguments and the use of contravariant type parameters as return types are not permitted.

Assemblies

See .NET Assemblies

Attributes (metadata)

Attributes allow you to specify metadata for assemblies, functions, parameters, classes, structures, enumerations or fields. These can contain information for the compiler, can be read out for the runtime environment or via reflection during runtime. In C # these are indicated with square brackets above the target. The STAThread attribute is e.g. B. required if a program should support COM interoperability - the error message is otherwise: "A controlling 'IUnknown' not equal to NULL was specified, but either the requested interface was not 'IUnknown' or the provider does not support COM aggregation."

[STAThread()]
public static void Main(string[] argumente)
{
    //...
}

Attributes themselves are classes that are derived from the Attributes class and can be freely defined.

Example:

[AttributeUsage(AttributeTargets.All)]
public class Autor : System.Attribute
{
    public int Age
    {
        get;
        set;
    }

    public Autor(string name)
    {
        //...
    }
}

Use:

[Autor("Name des Autors")]
[Autor("Name des 2. Autors",Age=20)]
[Version(1,0,0,0)]
public class IrgendeineKlasse()
{
    //...
}

Query of attributes:

Autor[] a = typeof(IrgendeineKlasse).GetCustomAttributes(typeof(Autor), false) as Autor[];

Exceptions / Exceptions

Scheme:

try {
    // öffnet den Block um den Codeteil, dessen
    // Fehler abgefangen werden sollen.
    // ...unsichere Verarbeitung...
}
catch (ExceptionTypException exception_data) {
    // Fängt alle Exceptions vom Typ ExceptionTypException
    // ...tut was wenn ExceptionTypException geworfen wurde...
}
catch (Exception ex){
    //Fängt alle Exceptions welche von Exception abgeleitet wurden
    // ...tut was wenn Exception geworfen wurde...
}
finally {
    // Wird zwingend ausgeführt
}

It is not absolutely necessary that all blocks ( catch, finally) are always specified. Depending on the circumstances, a try-Block can also be followed directly by a finally-Block if, for example, no handling of an exception is desired. In addition, it is not absolutely necessary that a try- catchconstruct finallyhas to be followed by a - block. However, it is not possible tryto define just one block. A tryblock must be followed by at least one further block.

It is also possible catchto define several blocks. If tryan exception is thrown in the area, then all existing catchblocks are run through in order to see which block deals with the exception. This makes it possible to specifically program different reactions to exceptions.

If an exception is not catchcaught by a block, it is passed on to the next higher level.

Examples of predefined exception classes:

  • Exception
  • SystemException
  • IndexOutOfRangeException
  • NullReferenceException
  • InvalidOperationException
  • ArgumentException
  • ArgumentNullException
  • ArithmeticException
  • ArithmeticOutOfRangeException
  • OverflowException
  • DllNotFoundException

When implementing your own, non-critical exceptions, make sure not to Exceptionderive from the class , but from ApplicationException.

Throw

Means throwit is possible in a one catchblock, collected except one level to "throw" (pass). It is thus possible that the following code is also informed of the exception that has occurred and can thus take actions in turn to react to the exception. The call stack is retained.

A thrownew exception can also be triggered by, for example a program-specific exception that is not covered by existing C # exceptions.

Unsafe code

Through the code verification and .NET access verification be prevented in C # memory access error. If pointers are used, these security mechanisms are bypassed. This is only possible in the "Unsafe Code" operating mode.

Example:

using System;
class us_code {
    public static void Main() {
        unsafe {
            int i = 1; // i hat den Wert 1
            int* p = &i; // p zeigt auf den Speicher von i mit dem Wert 1
            Console.WriteLine("p = " + *p + ", i = " + i);
            i = 2;   // Sowohl i als auch *p haben nun den Wert 2...
            Console.WriteLine("p = " + *p + ", i = " + i);
        }
    }
}

In addition, classes and methods can be declared as unsafe .

Comments and documentation

In C #, as in C, C ++ and Java, both line and block comments are possible.

Line comments begin with two consecutive slashes ( // ) and end in the same line with a line break . Everything that follows the slash is treated as a comment up to the end of the line and is ignored by the compiler .

Block comments, which can extend over several lines, begin with the character combination / * and end with * / .

Both line and block comments can begin at the beginning or in the middle of a line. Block comments can end on the same line and can be followed by source code that is evaluated by the compiler. Everything within the block comment is ignored by the compiler.

// Dies ist ein Zeilenkommentar, der mit dem Zeilenumbruch endet
System.Console.WriteLine("Ein Befehl"); // Ein Zeilenkommentar am Zeilenende
/* Dies ist ein Blockkommentar, der in der gleichen Zeile endet */
System.Console.WriteLine("Ein weiterer Befehl"); /* Ein mehrzeiliger
Blockkommentar */System.Console.WriteLine("Noch ein Befehl");
System.Console.WriteLine("Befehl 1"); /* Kommentar */ System.Console.WriteLine("Befehl 2");
System.Console/* Kommentar */.WriteLine( /* Kommentar */ "..." )

Note: Comments are also possible within an instruction or declaration, e.g. B. to comment on individual method parameters. These types of comments should be avoided for readability and maintainability.

For the documentation of methods, C # provides a mechanism in the form of metadata (attributes) that enables XML- based documentation to be generated.

A special form of line comments is available for documenting types (that is, classes and their elements such as attributes or methods). For this purpose, the line comment begins with another, third slash ( /// ) and is located directly above the type to be documented (e.g. a method). This is followed by XML tags, each of which has a specific function in the documentation, for example a summary using a summary tag .

/// <summary>
/// Diese Funktion gibt den größeren Betrag zurück
/// </summary>
/// <param name="a">Der erste Übergabeparameter</param>
/// <param name="b">Der zweite Übergabeparameter</param>
/// <returns>Die Zahl mit dem größeren Betrag</returns>
/// <remarks>Diese Funktion gibt den größeren Betrag der beiden Übergebenen <see cref="Int32"/>zurück.
/// Sind die Beträge gleich groß, ist dies ein Fehler</remarks>
/// <exception cref="ArgumentException">Der Betrag der beiden Zahlen ist gleich groß</exception>
public int GetGreaterAbs(int a, int b)
{
    return Math.Max(Math.Abs(a), Math.Abs(b));
}

Alternatively, an external resource that contains the documentation can also be referenced:

/// <include file='xml_include_tag.doc' path='MyDocs/MyMembers[@name="test"]/*' />

Documentation in the XMLDoc API documentation format is viewed by the compiler as a normal line comment. Comments that begin with three slashes are only recognized as XMLDoc and extracted from the source code into a file when the compiler option “/ doc” is specified. This can then be further processed, e.g. For example, similar to Javadoc, it can be used to create HTML help. (NDoc)

keywords

Reserved keywords

The following identifiers are reserved keywords and must not be used for your own identifiers unless they are @preceded by a -sign (for example @else).

Contextual keywords

The following identifiers are context keywords , that is, within a valid context - and only there - they have a special meaning. However, they do not represent reserved words, which means that outside of their context they are allowed for their own identifiers.

add alias ascending
descending
equals
from
get global group
into
join
let
on orderby
partial
remove
select set
value var
where
yield

See also

literature

  • Andreas Kühnel: Visual C ♯ 2012 the comprehensive manual . 6th, updated and expanded edition. Galileo Press, Bonn 2013, ISBN 978-3-8362-1997-6 .

Individual evidence

  1. a b User-Defined Conditional Logical Operators. Microsoft Developer Network (MSDN), accessed April 25, 2011 .