Sei sulla pagina 1di 38

Operators

four specific operators ( sizeof , * , - > , and &) are available only in unsafe code C# uses different operators for assignment ( = ) and comparison ( == ) x = 3; if (x == 3) { } if (x = 3) // generate a compiler error { } C# s strict type - safety rules prevent the very common C error where assignment is performed instead of comparison in logical statements.

Operator Shortcuts
Demonstrate the difference between the prefix and postfix behavior : int x = 5; if (++x == 6) // true - x is incremented to 6 before the evaluation { Console.WriteLine(This will execute); } if (x++ == 7) // false - x is incremented to 7 after the evaluation { Console.WriteLine(This wont); }
x += 5; x = x + 5;

The Conditional Operator The conditional operator ( ?: ), also known as the ternary operator, is a shorthand form of the if...else construction condition ? true_value : false_value
int x = 1; string s = x + ; s += (x == 1 ? man : men); Console.WriteLine(s); // 1 man

The checked and unchecked Operators byte b = 255; b++; Console.WriteLine(b.ToString()); The byte data type can hold values only in the range zero to 255, so incrementing the value of b causes an overflow C# provides the checked and unchecked operators. If you mark a block of code as checked ,the CLR will enforce overflow checking, and throw an OverflowException if an overflow occurs Run this code, you will get an error message like this: byte b = 255; Unhandled Exception: System.OverflowException: checked Arithmetic operation resulted in an overflow. { b++; } at Wrox.ProCSharp.Basics.OverflowTest.Main(String[] args) Console.WriteLine(b.ToString());

If you want to suppress overflow checking, you can mark the code as unchecked : byte b = 255; unchecked { b++; } Console.WriteLine(b.ToString()); In this case, no exception will be raised, but you will lose data Note that unchecked is the default behaviour

The is Operator Check whether an object is compatible with a specific type The phrase is compatible means that an object either is of that type or is derived from that type int i = 10; if (i is object) { Console.WriteLine(i is an object); } All C# data types, inherits from object ; therefore the expression i is object will evaluate to true

The as Operator Perform explicit type conversions of reference types If the type being converted is compatible with the specified type, conversion is performed successfully If the types are incompatible, the as operator returns the value null object o1 = Some String; object o2 = 5; string s1 = o1 as string; // s1 = Some String string s2 = o2 as string; // s2 = null Perform a safe type conversion in a single step without the need to first test the type using the is operator and then perform the conversion

The sizeof Operator unsafe { Console.WriteLine(sizeof(int)); // 4 } Notice that you can use the sizeof operator only in unsafe code The typeof Operator Returns a System.Type object representing a specified type typeof(string) will return a Type object representing the System.String type

Nullable Types and Operators To define the value of the type as undefined int? a = null; int? b = a + 4; // b = null int? c = a * 5; // c = null if only one of the operands is null , the comparison will always equate to false int? a = null; int? b = -5; if (a > = b) // false Console.WriteLine(a > = b); else Console.WriteLine(a < b);

The Null Coalescing Operator If the first operand is not null , then the overall expression has the value of the first operand. If the first operand is null , then the overall expression has the value of the second operand int? a = null; int b; b = a ?? 10; // b has the value 10 a = 3; b = a ?? 10; // b has the value 3 The first operand must be a nullable type or reference type, and the second operand must be of same type as first or of a type that is implicitly convertible to type of first operand. If the second operand cannot be implicitly converted to type of first operand, a compile time error is generated.

Operator Precedence

Type Safety The Intermediate Language (IL) enforces strong type safety upon its code. Strong typing enables many of the services provided by .NET, including security and language interoperability Type Conversions need to convert data from one type to another
byte value1 = 10; byte value2 = 23; byte total; total = value1 + value2; Console.WriteLine(total);

Error: Cannot implicitly convert type int to byte The problem is when add 2 bytes together, the result will be returned as an int , not as another byte

Implicit Conversions Conversion between types can normally be achieved automatically If you store the result in a long instead of a byte , then no problems: byte value1 = 10; byte value2 = 23; long total; // this will compile fine total = value1 + value2; Console.WriteLine(total); This program has compiled with no errors at this point because a long holds more bytes of data than a byte , so there is no risk of data being lost.

The implicit type conversions supported in C#.

Explicit Conversions Many conversions cannot be implicitly made between types, and the compiler will give an error if any are attempted. These are some of the conversions that cannot be made implicitly: int to short Data loss is possible. int to uint Data loss is possible. uint to int Data loss is possible. float to int lose everything after the decimal point. Any numeric type to char Data loss is possible. decimal to any numeric type The decimal type is internally structured differently from both integers and floating - point numbers. int? to int The nullable type may have the value null .

explicitly carry out such conversions using casts


long val = 30000; int i = (int)val; // A valid cast. The maximum int is 2147483647

Casting can be a dangerous operation to undertake


long val = 3000000000; int i = (int)val; // An invalid cast. The maximum int is 2147483647

The result is -1294967296


long val = 3000000000; int i = checked((int)val); // throw overflow exception

Using casts, convert most primitive data types from one type to another
double price = 25.30; int approximatePrice = (int)(price + 0.5); // data lost after decimal point

convert an unsigned integer into a char


ushort c = 43; char symbol = (char)c; Console.WriteLine(symbol); // + sign

casting from a nullable to non - nullable type and the variable has the value null , an InvalidOperationException is thrown
int? a = null; int b = (int)a; // Will throw exception

convert between numeric and string


int i = 10; string s = i.ToString();

need to parse a string to retrieve a numeric or Boolean value


string s = 100; int i = int.Parse(s); Console.WriteLine(i + 50); // Add 50 to prove it is really an int

Parse() will register an error by throwing an exception if it is unable to convert the string

Boxing and Unboxing Boxing is the term used to describe the transformation of a value type to a reference type Unboxing , allow to reference type to a value type
int myIntNumber = 20; object myObject = myIntNumber; // Box the int int mySecondNumber = (int)myObject; // Unbox it back into an int

Warning: when unboxing, careful that the receiving value variable has enough room to store all the bytes in the value being unboxed. C# s int s, are only 32 bits long, so unboxing a long value (64 bits) into an int result in an InvalidCastException
long myLongNumber = 333333423; object myObject = (object)myLongNumber; int myIntNumber = (int)myObject;

Comparing Reference Types for Equality System.Object defines three different methods for comparing objects for equality: ReferenceEquals() and two versions of Equals() Using comparison operator ( == ) The ReferenceEquals() Method static method that tests whether two references refer to the same instance of a class, not possible to override return true if supplied with two references that refer to the same object instance, and false otherwise
SomeClass x, y; x = new SomeClass(); y = new SomeClass(); bool B1 = ReferenceEquals(null, null); // returns true bool B2 = ReferenceEquals(null,x); // returns false bool B3 = ReferenceEquals(x, y); // returns false because x and y // point to different objects

The virtual Equals() Method override it in your own classes in order to compare objects by value x.Equals(y); The static Equals() Method static version takes two parameters and compares them for equality provides an extra safeguard against throwing exceptions if there is a risk that an object might be null first checks whether the references it has been passed are null If they are both null , it returns true If just one of them is null , it returns false If both references actually refer to something, it calls the virtual instance version of Equals()

Comparison Operator (==) comparison operator as an intermediate option between strict value comparison and strict reference comparison
bool b = (x == y); // x, y object references

Comparing Value Types for Equality Microsoft has already overloaded the instance Equals() in System.ValueType class to test equality to value types
sA.Equals(sB)

where sA and sB are instances of some struct, the return value will be true or false override the comparison operator (sA==sB) to perform a value comparison ReferenceEquals() always returns false when applied to value types because, to call this method, the value types will need to be boxed into objects
bool b = ReferenceEquals(v,v); // v is a variable of some value type

Operator Overloading
Matrix a, b, c; // assume a, b and c have been initialized Matrix d = c * (a + b);

By overloading the operators, you can tell the compiler what + and * do when used in conjunction with a Matrix object How Operators Work
int myInteger = 3; uint myUnsignedInt = 2; double myDouble = 4.0; long myLong = myInteger + myUnsignedInt;

The compiler identifies the overload, called in the example takes two int parameters and returns an int ; this return value is subsequently converted to a long .

double myOtherDouble = myDouble + myInteger;

there is not an overload of the addition operator that takes this combination of parameters the compiler identifies the best matching overload of the addition operator as being the version that takes two double s as its parameters, and it implicitly casts the int to a double if the compiler finds something like this:
Vector vect1, vect2, vect3; // initialize vect1 and vect2 vect3 = vect1 + vect2; vect1 = vect1*2;

Compiler look for an overload of the addition operator, which takes two Vector instances as its parameters If the compiler cannot find a suitable overload, it will raise a compilation error

Operator Overloading Example: The Vector Struct


struct Vector { public double x, y, z; public Vector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public Vector(Vector rhs) public override string ToString() { { return ( + x + , + y + , + z + ); x = rhs.x; } y = rhs.y; } z = rhs.z; }

The operator overload that provides support for the addition operator:
public static Vector operator + (Vector lhs, Vector rhs) { Vector result = new Vector(lhs); result.x += rhs.x; result.y += rhs.y; result.z += rhs.z; return result; }

all operator overloads be declared as public and static , ie they are associated with their class or struct, not with a particular instance the body of the operator overload has no access to non static class members and has no access to the this identifier

static void Main() { Vector vect1, vect2, vect3; vect1 = new Vector(3.0, 3.0, 1.0); vect2 = new Vector(2.0, -4.0, -4.0); vect3 = vect1 + vect2; Console.WriteLine(vect1 = + vect1.ToString()); Console.WriteLine(vect2 = + vect2.ToString()); Console.WriteLine(vect3 = + vect3.ToString()); }

compiling and running it returns this result: Output: vect1 = ( 3 , 3 , 1 ) vect2 = ( 2 , -4 , -4 ) vect3 = ( 5 , -1 , -3 )

Multiplying a vector by a scalar public static Vector operator * (double lhs, Vector rhs) { return new Vector(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); } Console.WriteLine(2*vect3 = + 2*vect3); Multiplying two vectors public static double operator * (Vector lhs, Vector rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; } double dot = vect1*vect3; Console.WriteLine(vect1*vect3 = + dot);

Overloading the Comparison Operators C# has six comparison operators, and they come in three pairs: == and != > and < > = and < = The C# language requires that overload these operators in pairs, otherwise, get a compiler error
public static bool operator == (Vector lhs, Vector rhs) { if (lhs.x == rhs.x & & lhs.y == rhs.y & & lhs.z == rhs.z) return true; else return false; }

also need to override the != operator


public static bool operator != (Vector lhs, Vector rhs) { return ! (lhs == rhs); } static void Main() { Vector vect1, vect2, vect3; vect1 = new Vector(3.0, 3.0, -10.0); vect2 = new Vector(3.0, 3.0, -10.0); vect3 = new Vector(2.0, 3.0, 6.0); Console.WriteLine(vect1==vect2 returns + (vect1==vect2)); Console.WriteLine(vect1==vect3 returns + (vect1==vect3)); Console.WriteLine(); Console.WriteLine(vect1!=vect2 returns + (vect1!=vect2)); Console.WriteLine(vect1!=vect3 returns + (vect1!=vect3)); }

Compiling the program:


Microsoft (R) Visual C# 2008 Compiler version 3.05.20706.1 for Microsoft (R) .NET Framework version 3.5 Copyright (C) Microsoft Corporation. All rights reserved. Vectors3.cs(5,11): warning CS0660: Wrox.ProCSharp.OOCSharp.Vector defines operator == or operator != but does not override Object.Equals(object o) Vectors3.cs(5,11): warning CS0661: Wrox.ProCSharp.OOCSharp.Vector defines operator == or operator != but does not override Object.GetHashCode() vect1==vect2 returns True vect1==vect3 returns False vect1!=vect2 returns False vect1!=vect3 returns True

generates the following compiler warning because you haven t overridden Equals() for your Vector

Which Operators Can You Overload?

User - Defined Casts


C# allows two different types of casts: implicit and explicit int I = 3; long l = I; // implicit short s = (short)I; // explicit

explicit casts are required where there is a risk that the cast might fail or some data might be lost. The following are some examples:
1. When converting from an int to a short , the short might not be large enough to hold the value of the int . 2. When converting from signed to unsigned data types, incorrect results will be returned if the signed variable holds a negative value. 3. When converting from floating - point to integer data types, the fractional part of the number will be lost. 4. When converting from a nullable type to a non - nullable type, a value of null will cause an exception.

C# support casts to and from own data types (struct and class) define a cast as a member operator of one of the relevant classes cast operator must be marked as either implicit or explicit to indicate how you are intending it to be used If you know that the cast is always safe whatever the value held by the source variable, then you define it as implicit . If, however, you know there is a risk of something going wrong for certain values perhaps some loss of data or an exception being thrown then you should define the cast as explicit public static implicit operator float (Currency value) { // processing }

The cast defined here allows to implicitly convert the value of a Currency into a float If a conversion has been declared as implicit , the compiler will permit its use either implicitly or explicitly. If it has been declared as explicit , the compiler will only permit it to be used explicitly

Implementing User - Defined Casts use of implicit and explicit user - defined casts
struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) { this.Dollars = dollars; this.Cents = cents; } public override string ToString() { return string.Format(${0}.{1,-2:00}, Dollars,Cents); } }

convert Currency instances to float values, where the integer part of the float represents the dollars Currency balance = new Currency(10,50); float f = balance; // We want f to be set to 10.5 To be able to do this, need to define a cast public static implicit operator float (Currency value) { return value.Dollars + (value.Cents/100.0f); }

Potrebbero piacerti anche