Here, you will learn the role of implicit typing of local variables, partial methods, automatic properties, extension methods, anonymous types, and object initialization syntax.”... Road[r]
Trang 1Chapter 14 C# 2008
Language Features
Trang 2“With the release of NET 3.5, the C# language has been
programming constructs, many of which are used to enable the LINQ API (which you will begin to examine in Chapter 15) Here, you will learn the role of implicit typing of local variables, partial methods, automatic properties, extension methods, anonymous types, and object initialization syntax.”
Trang 314.1 Understanding Implicitly Typed Local Variables
14.2 Understanding Automatic Properties
14.3 Understanding Extension Methods
14.4 Understanding Partial Methods
14.5 Understanding Object Initializer Syntax
14.6 Understanding Anonymous Types
Trang 414.1 Understanding Implicitly Typed Local Variables
C# 2008 now provides a new keyword, var, which you can use in place of specifying a formal data type (such
as int, bool, or string) The compiler will automatically
infer the underlying data type based on the initial value used to initialize the local data point
static void DeclareImplicitVars() {
// Implicitly typed local variables // are declared as follows:
// var variableName = initialValue ; var myInt = 0 ;
var myBool = true ;
Trang 5Implicitly Typed Local Variables
In this case, the compiler is able to infer that myInt is in fact a System.Int32, myBool is a System.Boolean, and myString is indeed of type System.String, given the
initially assigned value
static void DeclareImplicitVars ()
{
// Implicitly typed local variables.
var myInt = 0 ;
var myBool = true ;
var myString = "Time, marches on ";
// Print out the underlying type.
Console WriteLine ("myInt is a: {0}", myInt GetType () Name );
Console WriteLine ("myBool is a: {0}", myBool GetType () Name );
Console WriteLine ("myString is a: {0}",
myString GetType () Name );
Trang 6Implicitly Typed Local Variables
You can use this implicit typing for any type including
arrays, generic types, and your own custom types
{
// More implicitly typed local variables.
var myMinivans = new List < MiniVan >();
Console WriteLine ("evenNumbers is a: {0}",
evenNumbers GetType () Name );
Trang 7Use of var Within foreach Constructs
Use of implicit typing within a foreach looping construct
static void VarInForeachLoop()
{
var evenNumbers = new int[] { 2, 4, 6, 8 };
// Use "var" in a standard foreach loop.
foreach (var item in evenNumbers) {
Console.WriteLine( "Item value: {0}" , item);
} }
Trang 8Use of var Within foreach Constructs
A foreach loop can make use of a strongly typed iterator when processing an implicitly defined local array
static void VarInForeachLoop ()
{
var evenNumbers = new int[] { 2 , 4 , 6 , 8 };
// Use a strongly typed System.Int32 to iterate over contents.
foreach (int item in evenNumbers )
{
Console WriteLine ("Item value: {0}", item );
}
Trang 9Restrictions on Implicitly Typed Variables
Implicit typing applies only to local variables in a method
or property scope
class ThisWillNeverCompile
{
// Error! var cannot be used as field data!
private var myInt = 10 ;
// Error! var cannot be used as a return value // or parameter type!
public var MyMethod ( var x , var y ){}
}
Trang 10Restrictions on Implicitly Typed Variables
Local variables declared with the var keyword must be assigned an initial value at the exact time of declaration and cannot be assigned the initial value of null
// Error! Must assign a value!
var myData ;
// Error! Must assign value at exact time of declaration!
var myInt ;
myInt = 0 ;
// Error! Can't assign null as initial value!
var myObj = null ;
Trang 11Restrictions on Implicitly Typed Variables
It is permissible to return an implicitly typed local variable
to the caller, provided that the method return type is the same underlying type as the var-defined data point
Cannot define a nullable implicitly typed local variable
using the C# ? token
// OK, is SportsCar is a reference type!
var myCar = new SportsCar ();
myCar = null ;
// Nope, can't define nullable implicit variables, // as implicit variables can never be initially assigned // null to begin with!
var ? nope = new SportsCar ();
Trang 12Implicitly Typed Local Arrays
Closely related to Implicitly Typed Local Variables
static void DeclareImplicitArrays()
Trang 13Implicitly Typed Local Arrays
The items in the array’s initialization list must be of the same underlying type (all ints, all strings, etc.)
An implicitly typed local array does not default to
System.Object
// Error! Mixed types!
var d = new [ ] { 1 , "one", 2 , "two", false };
Trang 14Implicit Typed Data Is Strongly Typed Data
Implicit typing of local variables results in strongly typed data
Type inference keeps the strongly typed aspect of the
C# language and affects only the declaration of variables
at compile time
static void ImplicitTypingIsStrongTyping () {
// The compiler knows "s" is a System.String.
var s = "This variable can only hold string data!";
s = "This is fine ";
Trang 15Usefulness of Implicitly Typed Local
Variables
Using var to declare local variables simply for the sake of doing so really brings little to the table
Doing so can be confusing to others reading your code
As it becomes harder to quickly determine the underlying data type
Therefore, if you know you need an int, declare an int!
Trang 1614.1 Understanding Implicitly Typed Local Variables
14.2 Understanding Automatic Properties
14.3 Understanding Extension Methods
14.4 Understanding Partial Methods
14.5 Understanding Object Initializer Syntax
14.6 Understanding Anonymous Types
Trang 1714.2 Understanding Automatic Properties
NET programming languages prefer the use of type
properties to safely obtain and assign private data fields
of a type, rather than using traditional GetXXX() and
public int PetName
{ get { return carName ; }
set { carName = value ; } } }
Trang 18Automatic Properties
C# 2008 now provides automatic property syntax
This feature will offload the work of defining a private backing field and the related C# property member to the compiler using a new bit of syntax
Under C# 2008, the previous Car type could now be
defined as
class Car { // Automatic property syntax.
public string PetName { get ; set ; } }
Trang 19public abstract string PetName { get ;
set ; } }
Trang 20Automatic properties
When defining automatic properties, you simply specify the access modifier, underlying data type, property
name, and empty get/set scopes
it is not possible to build read-only or write-only
automatic properties
// Read-only property? Error!
public int MyReadOnlyProp { get ; } // Write only property? Error!
public int MyWriteOnlyProp { set ; }
Trang 21Interacting with Automatic Properties
The class defining automatic properties will always need
to use property syntax to get and set the underlying
// No access to the private member in the defining // class Must use properties!
return string.Format( "PetName = {0}" , PetName);
} }
Trang 22Interacting with Automatic Properties
Assign and obtain the values using the expected
Trang 23Restricting Access on Automatic Properties
NET property can be constructed in such a way that the get and set logic is assigned a unique access modifier
For example, it is possible to define a public get scope and a more restrictive protected scope as
// Anyone can get the PetName value, but
// only the defining type and the children can set it.
public int PetName
{
get { return carName ; }
protected set { carName = value ; }
}
Trang 24Restricting Access on Automatic Properties
This same possibility is allowed using automatic property syntax as
With this update, the previous Main() method would now generate a compiler error when attempting to assign the value of the PetName property
public string PetName { get ; protected set ; }
static void Main( string [ ] args)
{
// Error! Setting the PetName is only possible
// from within the Car type or by a child type!
c.PetName = "Frank" ;
Trang 25Regarding Automatic Properties and
Default Values
If use automatic property syntax to wrap a reference
type, the hidden private reference type will also be set to
a default value of null
class Garage {
// The hidden int backing field is set to zero! public int NumberOfCars { get; set; }
// The hidden Car backing field is set to null! public Car MyAuto { get; set; }
}
Trang 26Regarding Automatic Properties and Default Values
Given C#’s default values for field data, you would be
able to print out the value of NumberOfCars as is, but if you directly invoke MyAuto, you will receive a null
reference exception
static void Main (string[ ] args ) {
Garage g = new Garage ();
// OK, prints defualt value of zero.
Console WriteLine ("Number of Cars: {0}",
g NumberOfCars );
// Runtime error! Backing field is currently null!
Trang 27Regarding Automatic Properties and Default Values
Given that the private backing fields are created at
compile time, you will be unable to make use of C# field initialization syntax to allocate the reference type directly with the new keyword
class Garage {
// The hidden backing field is set to zero!
public int NumberOfCars { get ; set ; }
// The hidden backing field is set to null!
public Car MyAuto { get ; set ; }
// Must use constructors to override default
// values assigned to hidden backing fields.
public Garage () {
MyAuto = new Car (); NumberOfCars = 1 ; }
public Garage ( Car car , int number ) {
MyAuto = car ; NumberOfCars = number ; }
Trang 2814.1 Understanding Implicitly Typed Local Variables
14.2 Understanding Automatic Properties
14.3 Understanding Extension Methods
14.4 Understanding Partial Methods
14.5 Understanding Object Initializer Syntax
14.6 Understanding Anonymous Types
Trang 2914.3 Understanding Extension Methods
Allow existing compiled types as well as types currently being compiled to gain new functionality without needing
to directly update the type being extended
Add functionality to precompiled types while providing the illusion these methods were there all along
With C# 2008, you can
define extension method
invoke extension methods on an instance level
invoke extension methods statically
Trang 30Defining Extension Methods
Create a new Console Application named
ExtensionMethods Now, assume you are authoring a utility class named MyExtensions that defines two
extension methods
The first method allows any object in the NET base
class libraries to have a brand-new method named
DisplayDefiningAssembly() that makes use of types in the System.Reflection namespace to display the
assembly of the specified type
The second extension method, named ReverseDigits(), allows any System.Int32 to obtain a new version of itself
Trang 31static class MyExtensions {
// This method allows any object to display the assembly it is defined in.
public static void DisplayDefiningAssembly( this object obj)
{
Console.WriteLine( "{0} lives here:\n\t->{1}\n" , obj.GetType().Name,
Assembly.GetAssembly(obj.GetType()));
}
// This method allows any integer to reverse its digits.
// For example, 56 would return 65.
public static int ReverseDigits( this int i)
{
// Translate int into a string, and then get all the characters.
char [ ] digits = i.ToString().ToCharArray();
// Now reverse items in the array.
Array.Reverse(digits);
// Put back into string.
string newDigits = new string (digits);
// return the modified string back as an int.return int.Parse(newDigits);
}
}
Example 14.1 Defining Extension Method
Trang 32Defining Extension Methods
Given that DisplayDefiningAssembly() has been
prototyped to extend System.Object, any type in any
assembly now has this new member
However, ReverseDigits() has been prototyped to only extend integer types, and therefore if anything other than
an integer attempts to invoke this method, you will
receive a compile-time error
A given extension method could have multiple parameters, but only the first parameter can be qualified with this
Trang 33Invoking Extension Methods on an Instance Level
All objects have a new method named
DisplayDefiningAssembly(), while System.Int32 types
(and only integers) have methods named
ReverseDigits() and Foo()
Trang 34static void Main( string [ ] args)
{
// The int has assumed a new identity!
int myInt = 12345678;
myInt.DisplayDefiningAssembly();
// So has the DataSet!
System.Data.DataSet d = new System.Data.DataSet();
d.DisplayDefiningAssembly();
// And the SoundPlayer!
System.Media.SoundPlayer sp = new System.Media.SoundPlayer();
sp.DisplayDefiningAssembly();
// Use new integer functionality.
Console.WriteLine( "Value of myInt: {0}" , myInt);
Console.WriteLine( "Reversed digits of myInt: {0}" , myInt.ReverseDigits());
Trang 35Invoking Extension Methods Statically
Recall that the first parameter of an extension method is marked with the this keyword, followed by the type of
item the method is applicable to
If we peek at what is happening behind the scenes (as verified
by a tool such as ildasm.exe), we will find that the compiler simply calls the “normal” static method, passing in the variable calling the method as a parameter (e.g., it is the value of this)
Trang 36private static void Main (string[ ] args )
{
int myInt = 12345678 ;
MyExtensions DisplayDefiningAssembly ( myInt );
DataSet d = new DataSet ();
MyExtensions DisplayDefiningAssembly ( d );
SoundPlayer sp = new SoundPlayer ();
MyExtensions DisplayDefiningAssembly ( sp );
Console WriteLine ( "Value of myInt: {0}" , myInt );
Console WriteLine ( "Reversed digits of myInt: {0}" ,
MyExtensions ReverseDigits ( myInt ));
TesterUtilClass Foo ( myInt );
TesterUtilClass Foo ( myInt , "Ints that Foo? Who would have thought
Trang 37The Scope of an Extension Method
Extension methods do not have direct access to the
members of the type they are extending
Consider the following simple Car type
public class Car
{ public int Speed ; public int SpeedUp () {
return ++ Speed ; }
}
Trang 38The Scope of an Extension Method
If you were to build an extension method for the Car type named SlowDown(), you do not have direct access to
the members of Car within the scope of the extension
method as we are not performing an act of classical
inheritance
public static class CarExtensions
{ public static int SlowDown ( this Car c ) {
// Error! This method is not deriving from Car!
return Speed ;
Trang 39The Scope of an Extension Method
The problem here is that the static SlowDown()
extension method is attempting to access the Speed
field of the Car type
Because SlowDown() is a static member of the CarExtensions class, Speed does not exist in this context!
Permissible to make use of the thisqualified parameter to access all public members (and only the public members) of the type being extending public static class CarExtensions
{ public static int SlowDown ( this Car
c ) { // OK!
return c Speed ; }
Trang 40The Scope of an Extension Method
At this point, you could create a Car object and invoke the SpeedUp() and SlowDown() methods as follows
static void UseCar () {
Car c = new Car ();
Console WriteLine ("Speed: {0}",
c SpeedUp ());
Console WriteLine ("Speed: {0}",
c SlowDown ());
}