Overloaded Assignment Operator Derived Class In C#
The latest version of this topic can be found at Assignment Operators.
Assignment operators store a value in the object designated by the left operand. There are two kinds of assignment operations: simple assignment, in which the value of the second operand is stored in the object specified by the first operand, and compound assignment, in which an arithmetic, shift, or bitwise operation is performed prior to storing the result. All assignment operators in the following table except the = operator are compound assignment operators.
|=||Store the value of the second operand in the object specified by the first operand (simple assignment).|
|*=||Multiply the value of the first operand by the value of the second operand; store the result in the object specified by the first operand.|
|Divide the value of the first operand by the value of the second operand; store the result in the object specified by the first operand.|
|Take modulus of the first operand specified by the value of the second operand; store the result in the object specified by the first operand.|
|Add the value of the second operand to the value of the first operand; store the result in the object specified by the first operand.|
|–=||Subtract the value of the second operand from the value of the first operand; store the result in the object specified by the first operand.|
|<<=||Shift the value of the first operand left the number of bits specified by the value of the second operand; store the result in the object specified by the first operand.|
|>>=||Shift the value of the first operand right the number of bits specified by the value of the second operand; store the result in the object specified by the first operand.|
|&=||Obtain the bitwise AND of the first and second operands; store the result in the object specified by the first operand.|
|Obtain the bitwise exclusive OR of the first and second operands; store the result in the object specified by the first operand.|
|Obtain the bitwise inclusive OR of the first and second operands; store the result in the object specified by the first operand.|
Three of the compound assignment operators have text equivalents. They are:
There are two ways to access these operator keywords in your programs: include the header file , or compile with the /Za (Disable language extensions) compiler option.
The simple assignment operator (=) causes the value of the second operand to be stored in the object specified by the first operand. If both objects are of arithmetic types, the right operand is converted to the type of the left, prior to storing the value.
Objects of const and volatile types can be assigned to l-values of types that are just volatile or that are neither const nor volatile.
Assignment to objects of class type (struct, union, and class types) is performed by a function named operator=. The default behavior of this operator function is to perform a bitwise copy; however, this behavior can be modified using overloaded operators. (See Overloaded Operators for more information.)
An object of any unambiguously derived class from a given base class can be assigned to an object of the base class. The reverse is not true because there is an implicit conversion from derived class to base class but not from base class to derived class. For example:
Assignments to reference types behave as if the assignment were being made to the object to which the reference points.
For class-type objects, assignment is different from initialization. To illustrate how different assignment and initialization can be, consider the code
The preceding code shows an initializer; it calls the constructor for that takes an argument of type . Given the code
the assignment statement
can have one of the following effects:
Call the function operator= for , provided operator= is provided with a argument.
Call the explicit conversion function , if such a function exists.
Call a constructor , provided such a constructor exists, that takes a argument and copies the result.
The compound assignment operators, shown in the table in Assignment Operators, are specified in the form e1= e2, where e1 is a modifiable l-value not of const type and e2 is one of the following:
An arithmetic type
A pointer, if is + or –
The e1= e2 form behaves as e1= e1e2, but e1 is evaluated only once.
Compound assignment to an enumerated type generates an error message. If the left operand is of a pointer type, the right operand must be of a pointer type or it must be a constant expression that evaluates to 0. If the left operand is of an integral type, the right operand must not be of a pointer type.
The assignment operators return the value of the object specified by the left operand after the assignment. The resultant type is the type of the left operand. The result of an assignment expression is always an l-value. These operators have right-to-left associativity. The left operand must be a modifiable l-value.
In ANSI C, the result of an assignment expression is not an l-value. Therefore, the legal C++ expression is illegal in C.
Expressions with Binary Operators
C++ Built-in Operators, Precedence and Associativity
C Assignment Operators
The correct title of this article is C# syntax. The substitution or omission of the # is due to technical restrictions.
Main article: C Sharp (programming language)
This article describes the syntax of the C#programming language. The features described are compatible with .NET Framework and Mono.
An identifier is the name of an element in the code. There are certain standard naming conventions to follow when selecting names for elements.
An identifier can:
- start with an underscore: _
- contain an underscore: _
- contain a numeral: 0123456789
- contain both upper case and lower case Unicode letters. Case is sensitive (FOO is different from foo).
An identifier cannot:
- start with a numeral
- start with a symbol, unless it is a keyword (check Keywords)
- contain more than 511 characters
- contain @ sign in between or at the end
Keywords are predefined reserved words with special syntactic meaning. The language has two types of keyword — contextual and reserved. The reserved keywords such as or may only be used as keywords. The contextual keywords such as or are only treated as keywords in certain situations. If an identifier is needed which would be the same as a reserved keyword, it may be prefixed by the @ character to distinguish it. This facilitates reuse of .NET code written in other languages.
|C# keywords, reserved words|
|1, 2 These are not actually keywords, thus (unlike actual keywords) it is possible to define variables and types using these names, but they act like keywords in certain new language constructs introduced in C# 2.0(1) and 3.0(2).|
Using a keyword as an identifier:
|Characters escapes in strings|
|Unicode character||followed by the hexadecimal unicode code point|
|1Strings in C# are not null terminated|
- This is a feature of C# 7.0.
The underscore symbol separates digits in number values for readability purposes. Compiler will ignore it.
Generally, it may be put only between digit characters. It cannot be put at the beginning () or the end of the value ( or ), next to the decimal in floating point values (), next to the exponent character () and next to the type specifier ().
Variables are identifiers associated with values. They are declared by writing the variable's type and name, and are optionally initialized in the same statement.
Multiple variables of the same type can be declared and initialized in one statement.
Local variable type inference
- This is a feature of C# 3.0.
C# 3.0 introduced type inference, allowing the type specifier of a variable declaration to be replaced by the keyword , if its actual type can be statically determined from the initializer. This reduces repetition, especially for types with multiple generic type-parameters, and adheres more closely to the DRY principle.
Constants are immutable values.
When declaring a local variable or a field with the keyword as a prefix the value must be given when it is declared. After that it is locked and cannot change. They can either be declared in the context as a field or a local variable. Constants are implicitly static.
This shows all the uses of the keyword.
The keyword does a similar thing to fields. Like fields marked as they cannot change once initialized. The difference is that you can choose to initialize them in a constructor. This only works on fields. Read-only fields can either be members of an instance or static class members.
The operators are used to signify a code block and a new scope. Class members and the body of a method are examples of what can live inside these braces in various contexts.
Inside of method bodies you can use the braces to create new scopes like so:
A C# application consists of classes and their members. Classes and other types exist in namespaces but can also be nested inside other classes.
Whether it is a console or a graphical interface application, the program must have an entry point of some sort. The entry point of the C# application is the method. There can only be one, and it is a static method in a class. The method usually returns and is passed command-line arguments as an array of strings.
A method is also allowed to return an integer value if specified.
Namespaces are a part of a type name and they are used to group and/or distinguish named entities from other ones.
A namespace is defined like this:
The statement loads a specific namespace from a referenced assembly. It is usually placed in the top (or header) of a code file but it can be placed elsewhere if wanted, e.g. inside classes.
The statement can also be used to define another name for an existing namespace or type. This is sometimes useful when names are too long and less readable.
|Arithmetic||, , , ,|
|Logical (boolean and bitwise)||, , , , , , , ,|
|Relational (conditional)||, , , , ,|
|Assignment||, , , , , , , , , ,|
|Delegate concatenation and removal||,|
|Type information||, , ,|
|Overflow exception control||,|
|Indirection and Address||, , ,|
Some of the existing operators can be overloaded by writing an overload method.
These are the overloadable operators:
|, , , , , , ,||Unary operators|
|, , , , , , , , ,||Binary operators|
|, , , , ,||Comparison operators, must be overloaded in pairs|
- Assignment operators ( etc.) are combinations of a binary operator and the assignment operator () and will be evaluated using the ordinary operators, which can be overloaded.
- Cast operators () cannot be overloaded, but you can define conversion operators.
- Array indexing () operator is not overloadable, but you can define new indexers.
The cast operator is not overloadable but you can write a conversion operator method which lives in the target class. Conversion methods can define two varieties of operators, implicit and explicit conversion operators. The implicit operator will cast without specifying with the cast operator () and the explicit operator requires it to be used.
Implicit conversion operator
Explicit conversion operator
The operator will attempt to do a silent cast to a given type. If it succeeds it will return the object as the new type, if it fails it will return a null reference.
Null coalesce operator
- This is a feature of C# 2.0.
is shorthand for:
Meaning that if the content of variable is not null, that content will be returned, otherwise the content of variable is returned.
C# inherits most of the control structures of C/C++ and also adds new ones like the statement.
These structures control the flow of the program through given conditions.
The statement is entered when the given condition is true. Single-line case statements do not require block braces although it is mostly preferred by convention.
Simple one-line statement:
Multi-line with else-block (without any braces):
Recommended coding conventions for an if-statement.
The construct serves as a filter for different values. Each value leads to a "case". It is not allowed to fall through case sections and therefore the keyword is typically used to end a case. An unconditional in a case section can also be used to end a case. See also how statement can be used to fall through from one case to the next. Many cases may lead to the same code though. The default case handles all the other cases not handled by the construct.
Iteration statements are statements that are repeatedly executed when a given condition is evaluated as true.
The loop consists of three parts: declaration, condition and increment. Any of them can be left out as they are optional.
Is equivalent to this code represented with a statement, except here the variable is not local to the loop.
The statement is derived from the statement and makes use of a certain pattern described in C#'s language specification in order to obtain and use an enumerator of elements to iterate over.
Each item in the given collection will be returned and reachable in the context of the code block. When the block has been executed the next item will be returned until there are no items remaining.
Jump statements are inherited from C/C++ and ultimately assembly languages through it. They simply represent the jump-instructions of an assembly language that controls the flow of a program.
Labels and statement
Labels are given points in code that can be jumped to by using the statement.
The statement can be used in statements to jump from one case to another or to fall through from one case to the next.
The statement breaks out of the closest loop or statement. Execution continues in the statement after the terminated statement, if any.
The statement discontinues the current iteration of the current control statement and begins the next iteration.
The loop in the code above reads characters by calling , skipping the statements in the body of the loop if the characters are spaces.
Runtime exception handling method in C# is inherited from Java and C++.
The base class library has a class called from which all other exception classes are derived. An -object contains all the information about a specific exception and also the inner exceptions that were caused. Programmers may define their own exceptions by deriving from the class.
An exception can be thrown this way:
Exceptions are managed within blocks.
The statements within the block are executed, and if any of them throws an exception, execution of the block is discontinued and the exception is handled by the block. There may be multiple blocks, in which case the first block with an exception variable whose type matches the type of the thrown exception is executed.
If no block matches the type of the thrown exception, the execution of the outer block (or method) containing the statement is discontinued, and the exception is passed up and outside the containing block or method. The exception is propagated upwards through the call stack until a matching block is found within one of the currently active methods. If the exception propagates all the way up to the top-most method without a matching block being found, the entire program is terminated and a textual description of the exception is written to the standard output stream.
The statements within the block are always executed after the and blocks, whether or not an exception was thrown. Such blocks are useful for providing clean-up code.
Either a block, a block, or both, must follow the block.
C# is a statically typed language like C and C++. That means that every variable and constant gets a fixed type when it is being declared. There are two kinds of types: value types and reference types.
Instances of value types reside on the stack, i.e. they are bound to their variables. If you declare a variable for a value type the memory gets allocated directly. If the variable gets out of scope the object is destroyed with it.
Structures are more commonly known as structs. Structs are user-defined value types that are declared using the keyword. They are very similar to classes but are more suitable for lightweight types. Some important syntactical differences between a and a are presented later in this article.
The primitive data types are all structs.
These are the primitive datatypes.
|Type name||BCL equivalent||Value||Range||Size||Default value|
|integer||−128 through +127||8-bit (1-byte)|
|integer||−32,768 through +32,767||16-bit (2-byte)|
|integer||−2,147,483,648 through +2,147,483,647||32-bit (4-byte)|
|unsigned integer||0 through 255||8-bit (1-byte)|
|unsigned integer||0 through 65,535||16-bit (2-byte)|
|unsigned integer||0 through 4,294,967,295||32-bit (4-byte)|
|unsigned integer||0 through 18,446,744,073,709,551,615||64-bit (8-byte)|
|signed decimal number||−79,228,162,514,264,337,593,543,950,335 through|
|floating point number||±1.401298E−45 through ±3.402823E+38||32-bit (4-byte)|
|floating point number||±4.94065645841246E−324 through|
|single Unicode character||through||16-bit (2-byte)|
Note: () is not a struct and is not a primitive type.
Enumerated types () are named values representing integer values.
variables are initialized by default to zero. They can be assigned or initialized to the named values defined by the enumeration type.