Create and use types - Exam Ref 70-483: Programming in C# (2013)

Exam Ref 70-483: Programming in C# (2013)

Chapter 2. Create and use types

As a craftsman, knowing your tools is important to get the work done. When building object-oriented software, types are your tools. Being skilled in creating useful types can make the difference between a successful project and a disaster. C# offers all the basic building blocks you need to create types that can be the foundation of any software project.

In this chapter, you learn what C# has to offer when it comes to creating types. You learn how to create types that can be easily used by you and your coworkers, types that encapsulate their inner workings so that you can focus on using them in the best way possible.

After learning how to create types, you examine what it means for C# to be a managed environment and how you can play nicely when using unmanaged, external resources. Finally, you look at how to use some of the types that the .NET Framework offers, and you work with the built-in string type.

Objectives in this chapter:

§ Objective 2.1: Create types

§ Objective 2.2: Consume types

§ Objective 2.3: Enforce encapsulation

§ Objective 2.4: Create and implement a class hierarchy

§ Objective 2.5: Find, execute, and create types at runtime by using reflection

§ Objective 2.6: Manage the object life cycle

§ Objective 2.7: Manipulate strings

Objective 2.1: Create types

Writing an application consists of using and creating types. In C#, there are a lot of options for creating a type. Knowing your options will enable you to choose and use those options wisely. For example, using enums can make your code a lot more readable. Using generics can make your code much more flexible and save you a lot of time.

But with great power comes great responsibility. The power that C# offers you when you create types makes it easy to make mistakes. In this section, you will learn what C# has to offer and when you should use a particular feature to create meaningful types.

THIS OBJECTIVE COVERS HOW TO:

§ Choose what type to create.

§ Create your type so it can be easily used.

§ Extend existing types with new capabilities.

Choosing a type to create

In an object-oriented language such as C#, objects and types are the fundamental building blocks. A type can be seen as a blueprint for constructing an object. It shows what data can be used and what behavior is present. Creating these blueprints and then using them to construct new objects enables you to build complex, maintainable applications with a lot of functionality. C# enables you to choose which type to create depending on your situation.

Types in C#

The C# typing system contains three different categories:

§ Value types

§ Reference types

§ Pointer types

Pointer types are rarely used. You use them only when working with unsafe code and when you need to use pointer arithmetic.

MORE INFO: POINTERS AND UNSAFE CODE

For more information, read “unsafe (C# Reference)” at http://msdn.microsoft.com/en-us/library/chfa2zb8.aspx.

Creating enums

One basic type in C# is the enum. An enum (short for enumeration) offers you a nice way to create a list of possible options.

public enum Gender { Male, Female }

Of course, you can use a Boolean value such as IsMale that is true or false to represent the gender. But when reading and working with code that uses this Boolean value, you have to map the true and false values to the actual meaning of gender. Using an enum improves the readability and maintainability of your code.

By default, the first element has the value 0, and each successive element is increased by 1, but you can change the starting index and the underlying type. In the following example, you declare an enum that has a byte type that starts with the value 1. Of course this is something that you should communicate to your team so they know that you changed the default 'margin-top:7.5pt;margin-right:0cm;margin-bottom:7.5pt; margin-left:20.0pt;line-height:normal;vertical-align:baseline'>enum Days : byte { Sat = 1, Sun, Mon, Tue, Wed, Thu, Fri };

If you want to compare the Days enum to the underlying byte value, you have to cast it to a byte:

Days day = Days.Sat;

if ((byte)day == 1) { }

Enumerations can also be used with a special Flags attribute; you can use one enum to set multiple combinations of values. Example 2-1 shows how to do this.

Example 2-1. Using the FlagAttribute for an enum

[Flags]

enum Days

{

None = 0x0,

Sunday = 0x1,

Monday = 0x2,

Tuesday = 0x4,

Wednesday = 0x8,

Thursday = 0x10,

Friday = 0x20,

Saturday = 0x40

}

Days readingDays = Days.Monday | Days.Saturday;

MORE INFO: USING AND CREATING ATTRIBUTES

For more information on how to use attributes, see “Objective 2.5: Find, execute, and create types at runtime by reflection” later in this chapter.

Value and reference types

Enums are only the beginning. An enum is a special kind of value type. C# has value and reference types. Both are used extensively in the C# programming language and the .NET Framework. When creating a new type, it’s important to know the differences so you can make an informed decision.

You may have been programming for a while and are comfortable with using reference types, but still haven’t created a custom value type. As such, there is a lot of misinformation when it comes to value versus reference types. A reference type, as the name suggests, contains a reference to the real value; a value type contains the value directly. An example of a reference type is a uniform resource locator (URL). If you read something interesting on the web, you can send the URL to a friend and point to the correct location where the information is stored. A value type is different. If you read an interesting article in a magazine and you want to give it to your friend, you need to make a copy of the article and give it directly.

This same behavior can be seen with value and reference types. In the common language runtime (CLR) that underpins C#, there are two locations in which a type can be stored in memory: the heap and the stack. The value of a reference type is stored on the heap, and the address to this value is stored on the stack. Although a value type is normally stored on the stack, there are exceptions (for example, a class that contains a value type as one of its fields, a lambda expression that closes over a value type, or a value type that is boxed), but this is the general idea. The benefit of storing data on the stack is that it’s faster, smaller, and doesn’t need the attention of the garbage collector.

MORE INFO: THE GARBAGE COLLECTOR AND THE HEAP

For more information, read Objective 2.6: Manage the object life cycle section later in this chapter.

Value types are useful when it comes to small data structures that belong together. For example, a point with an x and y coordinate is a candidate for a value type. As a rule of thumb, you can check the following three criteria to determine whether you want to create a value type:

§ The object is small.

§ The object is logically immutable.

§ There are a lot of objects.

Of course, these are still vague requirements. The only one who can determine whether you need a value type is you. If you’re optimizing for speed or for memory usage, the only way to determine whether a value type can improve performance is by measuring.

All objects in C# inherit from System.Object. Value types, however, inherit from System.ValueType (which inherits from System.Object). System.ValueType overrides some of the default functions (such as GetHashCode, Equals, and ToString) to give them a more meaningful implementation for a value type.

In C#, you cannot directly inherit from System.ValueType. Instead, you can use the struct keyword to create a new value type. Example 2-2 shows how you can create a struct that contains the coordinates for a point.

Example 2-2. Creating a custom struct

public struct Point

{

public int x, y;

public Point(int p1, int p2)

{

x = p1;

y = p2;

}

}

Structs and classes can have methods, fields, properties, constructors, and other functionalities. You cannot, however, declare your own empty constructor for a struct. Also, structs cannot be used in an inheritance hierarchy (which saves you some memory bytes!).

EXAM TIP

It’s important to know the different types that are available in C#. A value type should be used for small, immutable objects that are used a lot. In all other situations, use a reference type.

Giving your types some body

After you have decided whether you want to create a value or a reference type, you can start thinking about the body of your type. You probably didn’t start out creating an empty struct or class, which isn’t that interesting. The goal of your new type should be to encapsulate both related data and behavior so that it can be used by other parts of your program.

The type you create is like a blueprint. You specify which data and behaviors it contains. After you have created your blueprint you can then use it to instantiate objects that follow the blueprint.

Adding behaviors

The actions that your class exposes should be the core reason for its existence. You should think of your new class in terms of what it can do, not of what data it stores. In C#, you add behavior to classes by using methods.

NOTE: “FUNCTION” VERSUS “METHOD”

Sometimes the term “function” is used, and sometimes the term “method” is used. A function’s meaning implies that it returns a value and doesn’t modify anything in the system. You can say a function is the “read” part of the system. This is what is still used in functional languages such as F#, in which immutable data structures are popular.

A method does enable data modification and doesn’t return any data. This is the “write” part of the system.

You can see this distinction in the Func<T> and Action types that were added to the .NET Framework. Func<T> always has a return type, whereas Action returns nothing.

In C#, this distinction is not used. The preferred term is “method.”

Methods can be called by other parts of your code to execute behaviors. For example, a basic Hello World console application shows how this works, as you can see in Example 2-3.

Example 2-3. Calling a method

namespace CallingAMethod

{

class Program

{

static void Main(string[] args)

{

System.Console.WriteLine("I'm calling a method!");

}

}

}

The call to Console.WriteLine executes the function named WriteLine on the Console class and passes it a string argument.

The important steps when adding a method to your own type are the following:

1. Choose a name for your method.

2. Decide which data should be passed to your method.

3. Decide which data you want to return to the caller.

4. Decide on the visibility of your method.

If you want to create a Calculator class that needs to be able to add two numbers together, what would your method signature look like? Example 2-4 shows a basic implementation of the Calculator.

Example 2-4. Creating a method

class Calculator

{

public int Add(int x, int y)

{

return x + y;

}

}

The name Add clearly states what the method does. When reading the method name of a class, you should be able to understand what it does without looking at the implementation.

This improves the quality of your code. When writing code in a procedural style, you can fall into the trap of creating one big method that runs all kinds of actions. This results in code that’s less readable and maintainable. Object-oriented software is a paradigm shift from procedural coding. By using objects, you can create code that’s more meaningful and should be easier to maintain. But it can still happen that you write object-oriented applications as if they are procedural. Breaking large, complex methods into their individual parts and naming those methods wisely is one of the steps you can take to avoid procedural methods in an object-oriented system. Make sure your method name is focused on one thing. If your method starts growing and you have difficulty naming it, think about simplifying your method by splitting it into multiple methods that each has its own specific goal.

The Add method has two arguments: x and y. Naming your parameters is also important. When using Visual Studio, a user of your method (that could be you in a couple of days, weeks, or months!) often relies on IntelliSense to check which arguments your method expects. A good parameter name helps others use your method because it’s clear to them what you expect.

When deciding on which arguments to take, it’s important to think about what you will do with the data. Look at the examples shown in Example 2-5 and Example 2-6.

Example 2-5. Passing a complete customer to a method

public Distance CalculateDistanceTo(Customer customer)

{

Distance result = ... // Some difficult calculation that uses customer.Address

return result;

}

Example 2-6. Passing only an address to a method

public Distance CalculateDistanceTo(Address address)

{

Distance result = ... // Some difficult calculation that uses address

return result;

}

Which method should you choose? The problem with the one in Example 2-5 is that the Customer object is only used to retrieve the address. Suddenly the distance calculation algorithm is coupled to a Customer. Changes in the Customer class, such as adding a shipping address and a billing address, will ripple through to this class.

Example 2-6 asks for only the data it needs. This is a clearer and simpler design that improves maintainability.

MORE INFO: LAW OF DEMETER

This pattern is called the Law of Demeter. For more information on choosing your arguments read “Law of Demeter” at http://msdn.microsoft.com/en-us/magazine/cc947917.aspx#id0070040.

Named arguments, optional arguments, and overloading visibility

A new feature in C# 4 is the use of named and optional arguments. Optional arguments enable you to omit arguments for some parameters. Named arguments are useful to specify which arguments you want to pass a value. Named and optional arguments can be used with methods, indexers, constructors, and delegates. Optional arguments can be useful when you want to specify default values for certain parameters. Example 2-7 shows how to use named and optional arguments.

Example 2-7. Using named and optional arguments

void MyMethod(int firstArgument, string secondArgument = "default value",

bool thirdArgument = false) { }

void CallingMethod()

{

MyMethod(1, thirdArgument: true);

}

It’s also possible to create methods with the same name that differ in the arguments they take, which is called overloading. An example in the .NET Framework is Console.WriteLine. This method has 19 different overloads that take different parameters. You create an overload for a method by using the exact same name and return type, but by differing in the arguments you expect.

When creating a method, you can choose whether you want to return a value to the caller. If you don’t want to return a value, you can declare the return type of a method as void. If you do want to return a value, you need to specify the type that you want to return. Example 2-8 shows how to create a void method and a method that returns data.

Example 2-8. Returning data from a method

public void MethodWithoutAnyReturnValue()

{ /* Don't return any value to the caller */ }

public int MethodWithReturnValue()

{

return 42;

}

When creating a method, it’s important to decide on the visibility of the method. When you don’t declare an access modifier, your method is private by default. The other options you can use are public, internal, protected, and internal protected (for a class you can use public or internal). The default is internal.

MORE INFO: ACCESS MODIFIERS

For more information on access modifiers, read Objective 2.3: Enforce encapsulation later in this chapter.

Adding some data

A method such as Add in the Calculator class uses only data that’s passed into the method. A class can contain both behavior and data, however. The easiest way to store data inside a class is to use a field, which offers direct access to the value it stores. Example 2-9 shows how to use a field.

Example 2-9. Declaring and using a field

public class MyClass

{

public string MyInstanceField;

public string Concatenate(string valueToAppend)

{

return MyInstanceField + valueToAppend;

}

}

MyClass instance = new MyClass();

instance.MyInstanceField = "Some New Value";

Just as with methods, fields can have an access modifier, a type, and a name. Choosing the name is as important as the name of a method. Clear, descriptive names improve code quality and readability.

A field can be marked as read-only, which allows the field to be set only once during construction of the object. You can do this in the constructor or as a part of declaring your variable. After this, the field’s value cannot be changed.

If the fields value is set at compile time, you can also mark it as const. That way, the compiler knows for sure that the value will never change and it can perform some optimization. This also ensures that you don’t accidentally overwrite the value of the field. All assignments to the field in your code will result in a compile error.

Another way to access data in a class is with an indexer, which enables instances of your class or struct to be used like arrays. This is useful when your object contains data that resembles a collection.

For example, imagine that you are creating a class to play a card game. You have a Deck class that contains a collection of cards. Example 2-10 shows an example implementation.

Example 2-10. Creating a collection such as a Deck class

class Card {}

class Deck

{

public ICollection<Card> Cards { get; private set; }

}

If you want to access a specific card, you can go through the Cards property to access it, but this is not always convenient. That’s why C# has indexer properties. An indexer property allows your class to be accessed with an index, just like a regular array. An example of an indexer property in the .NET Framework can be found in the List<T> collection type. You can declare a new List<int>, initialize it with some values, and access the first item in the following way:

List<int> myList = new List<int>() { 1, 3, 5 };

myList[0] = 1;

You can access the elements inside the list by using the [] notations. You can also implement this on your own classes. For the Deck class, use the following:

public Card this[int index]

{

get { return Cards.ElementAt(index); }

}

Until now, all properties are declared as instance fields. Each object instance has its own properties, and the values of each one are independent of other instances. When you want to store data that’s not specific for an instance, you can make it static, like so:

class MyClass

{

public static int MyStaticField = 42;

}

The MyStaticField is not coupled to one specific instance of MyClass. To access the field, you don’t have to create a new instance. Instead, you can directly access it:

MyClass.MyStaticField = 43;

A static field or property is shared with all code that has access to it. This can have some dangerous side effects because if you change a static value in one place, it will change for all code that has access to the field.

Static can also be used for methods. The same idea applies that you don’t have to create an instance of a class to access a static method. If all methods in a class are static, the whole class can be declared static.

Using a blueprint

After you have created your type with all its properties and methods, you can start using it. Creating an instance of a class is done with the new operator. Calling new on an object executes that object’s constructor. By default, each class has an empty constructor that can be called. You can also define your own constructors. You do this for two reasons:

§ To initialize the type with data

§ To run some initialization code

Look at the card example in Example 2-11.

Example 2-11. Adding a constructor to your type

class Deck

{

private int _maximumNumberOfCards;

public List<Card> Cards { get; set; }

public Deck(int maximumNumberOfCards)

{

this.maximumNumberOfCards = maximumNumberOfCards;

Cards = new List<Card>();

}

// Rest of the class

}

As you can see in Example 2-11, the constructor takes an argument of int. When you want to instantiate a new instance of this class, you need to pass a value to the constructor:

Deck deck = new Deck(5);

The constructor also runs some code to make sure that the object is in a usable state. Some good practices when designing your constructors are these:

§ Explicitly declare the public default construct in classes if such a constructor is required.

§ Ensure that your constructor takes as few parameters as possible.

§ Map constructor parameters to properties in your class.

§ Throw exceptions from instance constructors if appropriate.

§ Do not call virtual members from an object inside its constructor.

MORE INFO: BEST PRACTICES FOR DESIGNING CONSTRUCTORS

If you want to know more best practices for designing your constructors, see http://msdn.microsoft.com/en-us/library/vstudio/ms229060(v=vs.100).aspx.

A class can also have multiple constructors, which can be chained to each other so you can avoid duplicate code. Example 2-12 shows how you can chain multiple constructors.

Example 2-12. Chaining constructors

class ConstructorChaining

{

private int _p;

public ConstructorChaining() : this(3) { }

public ConstructorChaining(int p)

{

this._p = p;

}

}

In production code it’s important that you make clear which constructor users of your class should use. You can do this by picking meaningful parameter names and by adding comments that explain the use of each constructor.

Designing classes

When designing a new class, you should keep in mind a couple of key design principles. These principles help you design classes that can be easily used, maintained, and extended. Not adhering to these principles can lead to a code base that starts to rot and eventually becomes a big ball of mud—a system that lacks a perceivable architecture.

All the principles come down to making sure that your code has two characteristics:

§ High cohesion

§ Low coupling

These principles mean that code shouldn’t depend on other code when it’s not absolutely necessary. This enables you to make changes to code without worrying that your changes will ripple through code and affect other subsystems.

An important list of principles is represented by the acronym SOLID. Table 2-1 explains what each initial stands for.

Table 2-1. SOLID design principles

Initial

Stands for

Description

S

Single responsibility principle

A class should have only one responsibility. For example, a class shouldn’t be both responsible for saving itself to the database and for displaying to the user.

O

Open/closed principle

An object should be open for extension but closed for modification. For example, by using a common interface, new objects can integrate with existing code without modifying the existing code.

L

Liskov substitution principle

A base type should be replaceable with subtypes in each and every situation. For example, a Duck that can swim and an inherited ElectricDuck that can swim only if the batteries are full. Suddenly, code needs to check whether the Duck is an ElectricDuck to replace empty batteries.

I

Interface segregation principle

Use client-specific interfaces instead of one general interface. A user of an interface should not have to implement all kinds of methods that he doesn’t use.

D

Dependency Inversion principle

Depend upon abstractions, not concretions. For example, when you use SomeServiceType inside your class, you shouldn’t depend on the actual implementation of SomeServiceType. Instead you should depend on an interface or abstract class. This way, you are less coupled to the actual implementation.

MORE INFO: DESIGN PRINCIPLES

If you want to know more about the big ball of mud principle, see http://www.codinghorror.com/blog/2007/11/the-big-ball-of-mud-and-other-architectural-disasters.html.

Other information about designing systems with high cohesion and low coupling can be found at http://msdn.microsoft.com/en-us/magazine/cc947917.aspx.

Uncle Bob, a prominent figure in the software industry, has an excellent website on the SOLID principles; see http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod.

Using generic types

A new feature added in C# 2.0 is generics. A generic type uses a Type parameter instead of hard-coding all the types.

One area in the .NET Framework in which you can see the use of generics is in the support for Nullables. A reference type can have an actual value of null, meaning it has no value. A value type can’t have a value of null, however. For example, how would you express that some Boolean value is true, false, or unknown? A regular Boolean can be only true or false.

This is why Nullables were added to the .NET Framework. A Nullable is a wrapper around a value type with a Boolean flag that it stores if the Nullable has a value set.

Example 2-13 is a simplified version of the support for Nullables in the .NET Framework.

Example 2-13. Generic Nullable<T> implementation

struct Nullable<T> where T : struct

{

private bool hasValue;

private T value;

public Nullable(T value)

{

this.hasValue = true;

this.value = value;

}

public bool HasValue { get { return this.hasValue; } }

public T Value

{

get

{

if (!this.HasValue) throw new ArgumentException();

return this.value;

}

}

public T GetValueOrDefault()

{

return this.value;

}

}

Instead of creating a Nullable type for each possible value type, there is now only one implementation that uses a generic type parameter to make it more flexible. This way, generics can be used to promote code reuse.

Normally, a value type would need to be boxed to be used in a nongeneric collection. By using generics, you can avoid the performance penalty for boxing and unboxing.

MORE INFO: BOXING AND UNBOXING

For more information, see Objective 2.2: Consume types later in this chapter.

The .NET Framework has several generic implementations of collection classes in the System.Collections.Generic namespace. Whenever possible, those generic collections should be used in favor of their nongeneric counterparts.

C# has a lot of possibilities when using generics. They can be used on structs, classes, interfaces, methods, properties, and delegates. You can even specify multiple generic type parameters when necessary.

As you can see in the example of Nullable<T>, a generic type parameter can also be constrained. In the case of Nullable<T>, it wouldn’t make any sense if T could be a reference type. Reference types by their nature already have the option of being null.

C# lets you add a simple where clause that constrains a type parameter. In Table 2-2, you can see the different constraints you can use.

Table 2-2. Possible constraints for a generic type parameter

Constraint

Description

where T: struct

The type argument must be a value type (only Nullable is not allowed).

where T : class

The type argument must be a reference type: for example, a class, interface, delegate, or array.

where T : new()

The type must have a public default constructor.

where T : <base class name>

The type argument must be or derive from the specified base class.

where T : <interface name>

The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.

where T : U

The type argument supplied for T must be or derive from the argument supplied for U.

Example 2-14 shows how to add such a clause to your class definition.

Example 2-14. Using a where clause on a class definition

class MyClass<T>

where T : class, new()

{

public MyClass()

{

MyProperty = new T();

}

T MyProperty { get; set; }

}

When working with a reference type, the default value is null; with a value type such as int, it is 0. But what is the default value when working with a generic type parameter? In this case, you can use the special default(T) keyword. This gives you the default value for the specific type of T. Example 2-15 shows how to use this.

Example 2-15. Using default(T) with a generic type parameter

public void MyGenericMethod<T>()

{

T defaultValue = default(T);

}

Extending existing types

C# offers several ways to extend existing types without having to modify the existing code. In this section, you look at two different ways: extension methods and overriding.

Extension methods

In .NET 4.0, a new capability was added called extension methods, which enable you to add new capabilities to an existing type. You don’t need to make any modifications to the existing type; just bring the extension method into scope and you can call it like a regular instance method.

Extension methods need to be declared in a nongeneric, non-nested, static class. Example 2-16 shows the creation of an extension method.

Example 2-16. Creating an extension method

public class Product

{

public decimal Price { get; set; }

}

public static class MyExtensions

{

public static decimal Discount(this Product product)

{

return product.Price * .9M;

}

}

public class Calculator

{

public decimal CalculateDiscount(Product p)

{

return p.Discount();

}

}

Notice that the Discount method has this Product as its first argument. The special this keyword makes this method an extension method.

EXAM TIP

Remember that the difference between a regular static method and an extension method is the special this keyword for the first argument.

The nice thing is that an extension method cannot only be declared on a class or struct. It can also be declared on an interface (such as IEnumerable<T>). Normally, an interface wouldn’t have any implementation. With extension methods, however, you can add methods that will be available on every concrete implementation of the interface.

Language Integrated Query (LINQ) is one of the best examples of how you can use this technique to enhance existing code. Instead of having to add all the LINQ operators to each and every class, they are created as extension methods on the base interfaces of each collection type. This way, all collections can suddenly use LINQ.

MORE INFO: LINQ

For more information on LINQ, see Chapter 4.

Overriding Methods

Another way to extend an existing type is to use inheritance and overriding. When a method in a class is declared as virtual, you can override the method in a derived class. You can completely replace the existing functionality or you can add to the behavior of the base class. Example 2-17 shows how to override a method in a derived class to change some functionality.

Example 2-17. Overriding a virtual method

class Base

{

public virtual int MyMethod()

{

return 42;

}

}

class Derived : Base

{

public override int MyMethod()

{

return base.MyMethod() * 2;

}

}

Now you can extend classes without modifying the original code. When designing your classes, it’s important to plan for extension points such as these. You can disable inheritance by using the sealed keyword on a class or a method. When used on a class, you can’t derive other classes from it. When used on a method, derived classes can’t override the method. Example 2-18 shows how this works.

Example 2-18. Using the sealed keyword on a method

class Base

{

public virtual int MyMethod()

{

return 42;

}

}

class Derived : Base

{

public sealed override int MyMethod()

{

return base.MyMethod() * 2;

}

}

class Derived2 : Derived

{

// This line would give a compile error

// public override int MyMethod() { return 1;}

}

MORE INFO: INHERITANCE AND OVERRIDING

For more information on using inheritance, see Objective 2.4: Create and implement a class hierarchy later in this chapter.

THOUGHT EXPERIMENT

Creating a new web shop

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are tasked with creating the basic types for a new web shop. As a customer, you can search through the existing product database and compare different items by reviewing specifications and reviews from other users. The system should keep track of popular products and make recommendations to the customer. Of course, the customer can then select the products he wants and place an order. There are also some business rules that you need to be aware of. A new customer is not allowed to place an order that exceeds $500. An order should be at least $10 to qualify for free shipping. More business rules will be added, but are not clear at the moment. Answer the following questions for your manager:

1. Which basic types are you going to use to build your web shop?

2. How can you make sure that your types contain both behavior and data?

3. How can you improve the usability of your types?

Objective summary

§ Types in C# can be a value or a reference type.

§ Generic types use a type parameter to make the code more flexible.

§ Constructors, methods, properties, fields, and indexer properties can be used to create a type.

§ Optional and named parameters can be used when creating and calling methods.

§ Overloading methods enable a method to accept different parameters.

§ Extension methods can be used to add new functionality to an existing type.

§ Overriding enables you to redefine functionality from a base class in a derived class.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You are creating a new collection type and you want to make sure the elements in it can be easily accessed. What should you add to the type?

a. Constructor

b. Indexer property

c. Generic type parameter

d. Static property

2. You are creating a generic class that should work only with reference types. Which type constraint should you add?

a. where T : class

b. where T : struct

c. where T : new()

d. where T : IDisposable

3. You pass a struct variable into a method as an argument. The method changes the variable; however, when the method returns, the variable has not changed. What happened?

a. The variable was not initialized before it was passed in.

b. A value type cannot be changed inside a method.

c. Passing a value type makes a copy of the data. The original wasn’t changed.

d. The method didn’t return the changes.

Objective 2.2: Consume types

C# is for the most part a statically typed language. This means that the C# compiler will check the type of every expression. This helps you in finding errors like trying to store one type into another type (such as a string in an int). It could be that you know that a type can be used as another type and you want to tell it to the compiler. Because of this, you sometimes have to convert between types. But parts of C# aren’t statically typed. The new dynamic keyword in C# 4.0 enables you to override the compile-time checking and work with C# as a weakly typed language.

THIS OBJECTIVE COVERS HOW TO:

§ Box and unbox a value type.

§ Convert between different types.

§ Use the dynamic keyword.

Boxing and unboxing

When working with value types in C#, sometimes you’ll want to store a value type inside a reference type. For example, the .NET library contains a string.Concat method that adds the arguments it receives together and returns them as a string.

Assume that you call string.Concat with the following arguments:

string.Concat("To box or not box", 42, true);

If you put this code in Visual Studio and hover your mouse over the Concat function, you see that it takes three arguments, all of type object.

You might notice that you are dealing with a string, an int, and a Boolean value. This is where boxing comes in.

The example in Example 2-19 puts an int inside an object and then gets it back again.

Example 2-19. Boxing an integer value

int i = 42;

object o = i;

int x = (int)o;

The important difference between a value type and a reference type is that the value type stores its value directly. A reference type stores a reference that points to an object on the heap that contains the value.

So boxing is the process of taking a value type, putting it inside a new object on the heap, and storing a reference to it on the stack. Unboxing is the exact opposite: It takes the item from the heap and returns a value type that contains the value from the heap.

If you execute an invalid unbox operation, the runtime will throw an InvalidCastException. You won’t see the error at compile time because the compiler trusts you in making the right call. At runtime, however, the conversion fails, and an exception is thrown.

The only other important thing to know is that when boxing and unboxing happen (as shown in the example, unboxing is clear), you need to explicitly cast your object from a reference to a value type. Boxing, on the other hand, is not that obvious. For example, calling GetType always boxes your value type because GetType is defined only on an object and can’t be overridden. Boxing occurs in other situations, too. One that can come as a surprise is that a value type is boxed when you use it as an interface. This snippet boxes the value 3 so you can use it as an interface.

IFormattable x = 3;

There are some performance implications with each box and unbox operation. When using the nongeneric collections to store a value type, you have a lot of those operations. The boxing and unboxing operations can hurt performance; however, now that you have generic support in the .NET Framework, this is less of an issue because you can store value types in a collection without boxing them.

Converting between different types

Because C# is mostly a statically typed language, you can’t change the type of a variable after it is declared. Unless an explicit conversion exists, it’s not possible to convert one item to another. For example, converting an int to a double is allowed, but changing an Address into a Person isn’t allowed.

The process of converting one type to another is called type conversion. There are several different types of conversions:

§ Implicit conversions

§ Explicit conversions

§ User-defined conversions

§ Conversion with a helper class

Implicit conversions

An implicit conversion doesn’t need any special syntax. It can be executed because the compiler knows that the conversion is allowed and that it’s safe to convert.

A value type such as int can be stored as a double because an int can fit inside a double without losing any precision. The other way around is not implicitly possible. When converting a double to an int, you have to truncate or round the double, which might lead to some data loss. Example 2-20 shows an implicit conversion from int to double.

Example 2-20. Implicitly converting an integer to a double

int i = 42;

double d = i;

Another implicit conversion is that from a reference type to one of its base types. For example, each reference type can be stored inside an object because ultimately each reference type inherits from an object. If an object implements an interface, it can also be implicitly converted to the interface. Example 2-21 shows the implicit conversion from an object to one of its base types.

Example 2-21. Implicitly converting an object to a base type

HttpClient client = new HttpClient();

object o = client;

IDisposable d = client;

Explicit conversions

Because of the type safety of the C# language, the compiler protects you from all implicit conversions that are not safe, such as converting a double to an int. If you do want to make this conversion, you need to do it explicitly. This is called casting. Example 2-22 shows how to cast a double to an int explicitly.

Example 2-22. Casting a double to an int

double x = 1234.7;

int a;

// Cast double to int

a = (int)x; // a = 1234

As with implicit conversion, explicit conversions also exist for reference types. Where you can go implicitly from a derived type to a base type, you need to cast from a derived to a base type, as Example 2-23 shows.

Example 2-23. Explicitly casting a base type to a derived type

Object stream = new MemoryStream();

MemoryStream memoryStream = (MemoryStream)stream;

EXAM TIP

Make sure that you know the difference between an implicit and explicit conversion. An explicit conversion is called casting and always needs some special syntax.

User-defined conversions

When creating your own types, you can add support for both implicit and explicit conversions.

Suppose you are working on a Money class that encapsulates all kinds of rounding algorithms for working with different currencies. Example 2-24 shows some of the implicit and explicit conversion you can add.

Example 2-24. Implementing an implicit and explicit conversion operator

class Money

{

public Money(decimal amount)

{

Amount = amount;

}

public decimal Amount { get; set; }

public static implicit operator decimal(Money money)

{

return money.Amount;

}

public static explicit operator int(Money money)

{

return (int)money.Amount;

}

}

Now, when working with the Money class, you can use an implicit conversion to decimal and an explicit conversion to int, as Example 2-25 shows.

Example 2-25. Using an implicit and explicit cast operator on a custom type

Money m = new Money(42.42M);

decimal amount = m;

int truncatedAmount = (int)m;

Adding these kinds of conversion can really improve the usability of your type. As you can see, the implicit and explicit operator should be declared as a public static method on your class. You need to specify the return type (the type you are casting to) and the type you are casting from (an instance of your class).

Conversions with a helper class

The .NET Framework also offers helper classes for conversions between types. For converting between noncompatible types, you can use System.BitConverter. For conversion between compatible types, you can use System.Convert and the Parse or TryParse methods on various types. Example 2-26 shows how to use Parse and TryParse.

Example 2-26. Using the built-in Convert and Parse methods

int value = Convert.ToInt32("42");

value = int.Parse("42");

bool success = int.TryParse("42", out value);

When creating your own types, you can override ToString to return a string representation of your object. If necessary, you can then create a Parse and TryParse method that converts the string back to the original object. Implementing the IFormattable interface is required so that your object can be used by the Convert class.

MORE INFO: IFORMATTABLE INTERFACE

For more information on using the IFormattable interface, see Objective 2.7: Manipulate strings later in this chapter.

Confirming that a conversion is valid

Sometimes you get passed a type and you want to check whether you can convert it to some other type. This can happen when you are given a base type and you want to determine whether you can convert it to a derived type. Of course, you can always wrap it in a try/catch statement and catch the InvalidCastException, but that would decrease both the performance and the readability of your code.

C# has both, the is operator and the as operator that can be used to check whether a type can be converted to another type and to do so in a safe way. The is operator returns true or false, depending on whether the conversion is allowed. The as operator returns the converted value or null if the conversion is not possible. Example 2-27 shows how to use these operators.

Example 2-27. Using the is and as operators

void OpenConnection(DbConnection connection)

{

if (connection is SqlConnection)

{

// run some special code

}

}

void LogStream(Stream stream)

{

MemoryStream memoryStream = stream as MemoryStream;

if (memoryStream != null)

{

// ....

}

}

Using the as operator is more efficient when you want to use the value afterward. If you only want to check whether your type is of a certain type, you can use the is operator. The is and as operators can also be used on Nullable types.

Using dynamic types

As stated before, C# is a partially static typed language. The dynamic keyword, added in C# 4.0, is where you enter the world of weakly typed languages. Working in a weakly typed system is helpful when communicating with external resources (such as COM Interop, Iron-Python, JavaScript Object Notation (JSON) result sets, or the HTML Document Object Model [DOM]) or when working with reflection inside C#.

When the C# compiler encounters the dynamic keyword, it stops with statically type checking (for example, checking whether a method exists on a type or if it has certain arguments). Instead, the compiler saves the intent of the code so that it can be later executed at runtime. This is why using dynamic types won’t generate any compile-time errors, although it can certainly generate runtime errors.

Office automation APIs

When integrating with Component Object Model (COM) applications, you use a Primary Interop Assembly (PIA). PIAs are .NET assemblies that bridge the gap between .NET and COM. For example, Microsoft Office has the kinds of assemblies that enable you to integrate with Word, Excel, and other Office applications from your .NET application.

Before the dynamic keyword was added, doing some Office automation was not something you wanted to do as a hobby. But with the new dynamic keyword, accessing Office is a lot easier. Example 2-28 shows how the dynamic keyword enables you to export some data to Excel.

Example 2-28. Exporting some data to Excel

static void DisplayInExcel(IEnumerable<dynamic> entities)

{

var excelApp = new Excel.Application();

excelApp.Visible = true;

excelApp.Workbooks.Add();

dynamic workSheet = excelApp.ActiveSheet;

workSheet.Cells[1, "A"] = "Header A";

workSheet.Cells[1, "B"] = "Header B";

var row = 1;

foreach (var entity in entities)

{

row++;

workSheet.Cells[row, "A"] = entity.ColumnA;

workSheet.Cells[row, "B"] = entity.ColumnB;

}

workSheet.Columns[1].AutoFit();

workSheet.Columns[2].AutoFit();

}

var entities = new List<dynamic> {

new

{

ColumnA = 1,

ColumnB = "Foo"

},

new

{

ColumnA= 2,

ColumnB= "Bar"

}

};

DisplayInExcel(entities);

In this example, the type of workSheet is dynamic. The statements that use the worksheet variable are evaluated at runtime and dispatched to the Office application programming interfaces (APIs). All the type checking and necessary conversions take place at runtime.

DynamicObject and ExpandoObject

The .NET Framework offers two special classes when working with dynamic types: DynamicObject and ExpandoObject. DynamicObject is the most flexible. When inheriting from DynamicObject, you can override members that enable you to override operations such as getting or setting a member, calling a method, or performing conversions. By using DynamicObject, you can create truly dynamic objects and have full control over how they operate at runtime. Example 2-29 shows how to inherit from DynamicObject.

Example 2-29. Creating a custom DynamicObject

public class SampleObject : DynamicObject

{

public override bool TryGetMember(GetMemberBinder binder, out object result)

{

result = binder.Name;

return true;

}

}

dynamic obj = new SampleObject();

Console.WriteLine(obj.SomeProperty); // Displays 'SomeProperty'

ExpandoObject is a sealed implementation that enables you to get and set properties on a type. In ASP.NET Model-View-Controller (MVC), for example, there is a ViewBag that can be used to pass data from the Controller to the View. ViewBag is an ExpandoObject. Instead of creating a new, statically typed property for each data element you want to pass, you can use the ViewBag, as Example 2-30 shows.

Example 2-30. The dynamic keyword in ASP.NET MVC

public ActionResult Index()

{

ViewBag.MyDynamicValue = "This property is not statically typed";

return View();

}

The dynamic keyword should be used carefully. It gives you great flexibility, but because you lose static typing it can also easily lead to errors that can only be found at runtime. But when integrating with other languages or to replace reflection, the dynamic support is a nice addition to the .NET Framework.

THOUGHT EXPERIMENT

Optimizing your code

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are developing a reusable library for doing complex calculations. Your application is gaining popularity, but you are starting to hear some negative responses. Some say that your types cannot be used easily. When displaying the end results of calculations to the end user, there is a lot of manual work involved. Others experience performance problems and want you to do something about it. You started developing your application with C# 1.0, and your application uses ArrayLists to keep track of all the parameters needed for the calculations. Your parameters are implemented as a struct. Your algorithms are implemented in a class hierarchy, and you often need to cast a base type to a derived type. Because this isn’t always possible, you have added a lot of try/catch statements to recover from errors. Answer the following questions:

1. How can a generic collection improve performance?

2. Is there anything you can do to avoid the exceptions when converting between types?

3. How can you ensure your type is better converted to the basic CLR types?

Objective summary

§ Boxing occurs when a value type is treated as a reference type.

§ When converting between types, you can have an implicit or an explicit conversion.

§ An explicit conversion is called casting and requires special syntax.

§ You can create your own implicit and explicit user-defined conversions.

§ The .NET Framework offers several helper methods for converting types.

§ The dynamic keyword can be used to ease the static typing of C# and to improve interoperability with other languages.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You are creating a custom Distance class. You want to ease the conversion from your Distance class to a double. What should you add?

a. Nothing; this is already possible.

b. An implicit cast operator.

c. An explicit cast operator.

d. A static Parse method.

2. You want to determine whether the value of an object reference is derived from a particular type. Which C# language feature can you use? (Choose all that apply.)

a. An as operator

b. An implicit cast

c. An is operator

d. A dynamic keyword

3. You are using an ArrayList as a collection for a list of Points, which are a custom struct. You are experiencing performance problems when working with a large amount of Points. What do you have to do?

a. Use a generic collection instead of ArrayList.

b. Change Point to be a reference type.

c. Add an implicit conversion from Point to object.

d. Make the collection of type dynamic.

Objective 2.3: Enforce encapsulation

Encapsulation is one of the pillars of object-oriented development. Hiding the private elements from other objects inside an object-oriented system makes better software. When you want something done, ask another object to perform this action. The only thing you have to know is the external interface. The implementation is hidden from you, and you don’t have to understand how it works as long as it complies with the interface. C# helps with encapsulating the inner workings of an object by providing properties, access modifiers, and interfaces.

THIS OBJECTIVE COVERS HOW TO:

§ Use access modifiers.

§ Use properties.

§ Use explicit interface implementation.

Using access modifiers

One of the key concepts of encapsulation is hiding information. This is where the access modifiers of C# can be used. Access modifiers enable you to restrict access to all types and type members. Example 2-31 shows how to use the public access modifier on a class and a method.

Example 2-31. Using access modifiers

public class Dog

{

public void Bark() { }

}

Because of the public access modifier of the class Dog, everyone can create new instances of it. The Bark method is also public, which means everyone can call it. There are other access modifiers in C# that are more restrictive. Table 2-3 shows which access modifiers you can use in C#.

Table 2-3. Access modifiers in C#

Access modifier

Description

public

None; restricted access

internal

Limited to the current assembly

protected

Limited to the containing class and derived classes

protected internal

Limited to the current assembly or derived types

private

Limited to the containing type

Hiding with private

A private member can be accessed only by members within the same type. This is the most restricted access modifier. For nested members such as properties and methods, private is the default access modifier. However, mentioning it explicitly in your code can help other developers understand your intentions. Example 2-32 shows how to use the private access modifier on a field.

Example 2-32. Using the private access modifier

public class Accessibility

{

private string _myField;

public string MyProperty

{

get { return _myField; }

set{ _myField = value; }

}

}

The field _myField has an accessibility of private. This means that it can be accessed only inside the class. The public property MyProperty wraps access to the field.

This helps you to enforce business rules when users of your class try to change the value of your property. In your set accessor, you can see whether the new value is allowed and make sure that no one can change it to an illegal value.

Users of this class are also encapsulated from changes to the inner workings of the class. For example, when the implementation changes to a lookup for the value of MyProperty, outside users wouldn’t notice, as Example 2-33 shows.

Example 2-33. Changing a private field without outside users noticing

public class Accessibility

{

// initialization code and error checking omitted

private string[] _myField;

public string MyProperty

{

get { return _myField[0]; }

set { _myField[0] = value; }

}

}

Users of your class can still access MyProperty without even knowing that the implementation has changed.

Protecting accessibility in inheritance hierarchies

When working with a hierarchy of objects, there is another access modifier that can be used: protected. Protected restricts accessibility to members of the type and all classes that derive from it. It can be used on all members of a class. A member marked with private is not accessible by derived types. Example 2-34 shows the difference between private and protected in an inheritance hierarchy.

Example 2-34. Using the protected access modifier with inheritance

public class Base

{

private int _privateField = 42;

protected int _protectedField = 42;

private void MyPrivateMethod() { }

protected void MyProtectedMethod() { }

}

public class Derived : Base

{

public void MyDerivedMethod()

{

// _privateField = 41; // Not OK, this will generate a compile error

_protectedField = 43; // OK, protected fields can be accessed

// MyPrivateMethod(); // Not OK, this will generate a compile error

MyProtectedMethod(); // OK, protected methods can be accessed

}

}

MORE INFO: INHERITANCE

For more information on using inheritance, see Objective 2.4: Create and implement a class hierarchy later in this chapter.

Keeping types internal to your assembly

Code in C# is contained in assemblies. Inside Visual Studio, you can think of an assembly as a project. This is where another access modifier can be used: internal. Internal restricts access to a type or a type member to the same assembly. It is more restrictive than public, but less than private. Internal is useful when you have a type, such as a helper class or an implementation detail, that shouldn’t be accessible outside of the assembly you’re building. Example 2-35 shows how to use the internal access modifier.

Example 2-35. Using the internal access modifier

internal class MyInternalClass

{

public void MyMethod() { }

}

The class MyInternalClass can be used only inside the assembly where it’s declared. The public method inside the class is restricted by its enclosing type. So only users who have access to the internal class can call the public method.

You can also combine the protected and internal access modifier. When using the protected internal access modifier, keep in mind that it is or, not and. In practice, this means that access is limited to the current assembly or types derived from the class, even if those types are in another assembly.

When you want to expose internal types or type members to another assembly, you can use a special attribute: InternalsVisibleToAttribute. You can use this attribute to specify another assembly that can also access the internal types. One situation where this can be useful is when you write unit tests. Maybe you have an internal class that encapsulates a difficult algorithm and you want to write unit tests for it. Normally, you include this attribute inside the AssemblyInfo.cs file that’s stored in the Properties folder of your project:

[assembly:InternalsVisibleTo("Friend1a")]

[assembly:InternalsVisibleTo("Friend1b")]

You can use as many attributes as you need. In this example, the assemblies Friend1a and Friend1b are now allowed to access the internal types and members of your assembly.

Table 2-4 summarizes which access modifiers can be used in specific situations.

Table 2-4. Allowed access modifiers on nested types

Members of

Default member accessibility

Allowed declared accessibility of the member

enum

public

None

class

private

publicprotectedinternalprivateprotected internal

interface

public

None

struct

private

publicinternalprivate

MORE INFO: ATTRIBUTES

For more information on using attributes, see Objective 2.5: Find, execute, and create types at runtime by using reflection later in this chapter.

Something to keep in mind is that the access modifier of the enclosing type is always taken into account. For example, a public method inside an internal class has an accessibility of internal. There are exceptions to this (for example, when an internal class implements a public interface or when a class overrides a public virtual member of a base class), so you need to keep track of those things when determining the accessibility of a type you need.

It’s a good practice to always explicitly choose for the lowest visibility possible because you want to hide as much information as possible. If you don’t declare any access modifier, C# assigns a default member accessibility as defined in Table 2-4.

Using properties

A field offers direct access to the data it contains. In object-oriented development, however, you often prefer to have some control over your data. In some languages, this leads to the pattern shown in Example 2-36.

Example 2-36. Encapsulating a field with custom methods

private int _field;

public void SetValue(int value) { _field = value; }

public int GetValue() { return _field; }

In this way, you can run custom code when setting and getting a value. For example, you can see whether the new value is allowed or you can calculate the result on the fly.

C# offers a nice compact way to use this pattern: properties. A property looks like a regular field. It has a type, a name, and an access modifier. The difference is that it adds accessors.

Example 2-37 shows how to create a property.

Example 2-37. Creating a property

class Person

{

private string _firstName;

public string FirstName

{

get { return _firstName; }

set

{

if (string.IsNullOrWhiteSpace(value))

throw new ArgumentException();

_firstName = value;

}

}

}

As you can see, the get and set methods are part of the property definition. The field that contains the real data is private and can be accessed only through the property (except when inside the class).

Using a property with the default get and set methods is so common that C# added a shorthand notation for it:

public int Value { get; set; }

This is called an auto-implemented property. The compiler translates this into a property with a private, anonymous backing field. This can save you some time when you type a lot of properties. When you need some additional code to execute, the code is easily changed to use a get and setmethod.

The get and set accessor can have different access modifiers. You can, for example, have a public get and a private set method. For outside users, this creates the illusion of a read-only field. You can also have properties with only a get or only a set accessor. A single get accessor can come in handy when creating a true read-only property or when creating a property that calculates its value on the fly. A property with only a set accessor is a little uncommon, but you can use it in a “fire-and-forget” scenario where the user sets a value and never checks it.

EXAM TIP

Always favor properties over fields for public members. An automatically implemented property looks like a field to the outside world, but you can always add extra behavior when necessary.

Using explicit interface implementations

Interfaces are useful when using encapsulation. In the next objective, you look at how you can design and use interfaces. But regarding the topic of encapsulation, you need to know about explicit interface implementation.

As an example of explicit interface implementation, look at the Entity Framework (an object-relational mapper that’s part of the .NET Framework). When working with the Entity Framework, you work with a class DbContext, which is a wrapper around ObjectContext and exposes an easier-to-use interface.

DbContext implements the following interface (see Example 2-38).

Example 2-38. The IObjectContextAdapter interface

public interface IObjectContextAdapter

{

ObjectContext ObjectContext { get; }

}

Although the interface shows an ObjectContext property, the following code won’t compile:

DbContext ctx = ...; // create a new context

var context = ctx.ObjectContext;

The following will compile:

var adaptedContext = ((IObjectContextAdapter)ctx).ObjectContext;

How is this possible? It’s possible because DbContext implements the interface IObjectContextAdapter explicitly. Explicit interface implementation means that an interface type element can be accessed only when using the interface directly. You can create an explicit interface implementation by adding the interface name and a period to the implementation.

Example 2-39. Implementing an interface explicitly

interface IInterfaceA

{

void MyMethod();

}

class Implementation : IInterfaceA

{

void IInterfaceA.MyMethod() { }

}

The Implementation class implements the interface IInterfaceA explicitly. When you have an instance of Implementation, you can’t access MyMethod. But when you cast Implementation to IInterfaceA, you have access to MyMethod. In such a way, explicit interface implementation can be used to hide members of a class to outside users.

There is another situation in which explicit interface implementation is necessary: when a class implements two interfaces that contain duplicate method signatures but wants a different implementation for both. When implicitly implementing those two interfaces, only one method exists in the implementation. With explicit interface implementation, both interfaces have their own implementation. Example 2-40 shows how to implement an interface explicitly.

Example 2-40. Implementing an interface explicitly

interface ILeft

{

void Move();

}

interface IRight

{

void Move();

}

class MoveableOject : ILeft, IRight

{

void ILeft.Move() { }

void IRight.Move() { }

}

THOUGHT EXPERIMENT

Encapsulating types properly

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are working with a team to create a new application for keeping track of the inventory of a chemistry lab. Your application consists of lots of types that all participate in making sure that items are not used by unqualified personnel, dangerous chemicals are not used in the same room, and items are ordered as soon as the stock is running low.

Currently, all types in the system have different access modifiers. Some types are completely immutable; others expose all their data. You are discussing the current problems with a colleague. He argues that all types and all type members should be public.

1. Explain to your colleagues why making all members public isn’t the solution.

2. Give an example of how a property can help with encapsulating data while still improving usability.

3. How can interfaces be used to improve the design?

Objective summary

§ Encapsulation is important in object-oriented software. It hides internal details and improves the usability of a type.

§ Data can be encapsulated with a property.

§ Properties can have both a get and a set accessor that can run additional code, commonly known as getters and setters.

§ Types and type elements can have access modifiers to restrict accessibility.

§ The access modifiers are public, internal, protected, protected, internal, and private.

§ Explicit interface implementation can be used to hide information or to implement interfaces with duplicate member signatures.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. What access modifier should you use to make sure that a method in a class can only be accessed inside the same assembly by derived types?

a. Make the class public and its members public.

b. Make the class public and its members protected.

c. Make the class internal and its members internal.

d. Make the class internal and its members protected.

2. You need to expose some data from a class. The data can be read by other types but can be changed only by derived types. What should you use?

a. A protected field

b. A public property with a protected set modifier

c. A protected property

d. A protected property with a public get modifier

3. You have a class that implements two interfaces that both have a method with the same name. Interface IA should be the default implementation. Interface IB should be used only in special situations. How do you implement those interfaces?

a. Implement IA implicitly and IB explicitly.

b. Implement both IA and IB explicitly.

c. Implement both IA and IB implicitly.

d. Implement IA explicitly and IB implicitly.

Objective 2.4: Create and implement a class hierarchy

Inheritance is another pillar of object-oriented development. Inheritance is the process of letting one class derive from another class. Inheritance between a base and a derived class establishes an “is-a-kind-of” relationship. For example, a child is a human, and a SqlConnection is aDbConnection. This enables you to create hierarchies of objects that can be used to better model real-world scenarios. It also encourages code reuse. C# is a typical object-oriented language in that it offers all the support you need to create your own class hierarchies and to use them in the most efficient way. You will look at how using interfaces and base classes can help you create generic code that can work with multiple implementations, and you will look at how a base class can help with code reuse and some of the standard interfaces the .NET Framework offers.

THIS OBJECTIVE COVERS HOW TO:

§ Design and implement interfaces.

§ Create and use base classes.

§ Use some of the standard .NET Framework interfaces.

Designing and implementing interfaces

Interfaces are a key concept inside C#. An interface contains the public signature of methods, properties, events, and indexers. A type (both a class and a struct) can implement an interface. Example 2-41 shows how to implement a custom interface.

Example 2-41. Creating and implementing an interface

interface IExample

{

string GetResult();

int Value { get; set; }

event EventHandler ResultRetrieved;

int this[string index] { get; set; }

}

class ExampleImplementation : IExample

{

public string GetResult()

{

return "result";

}

public int Value { get; set; }

public event EventHandler CalculationPerformed;

public event EventHandler ResultRetrieved;

public int this[string index]

{

get

{

return 42;

}

set { }

}

}

The convention in C# is to let the name of an interface start with a capital I. This helps you when using IntelliSense to see if a type is an interface. As you can see in the example, no access modifiers are mentioned for the elements of the interface because all interface members are public by default. It’s not possible to implement an interface and change the access modifier to anything other than public.

One thing to note is that an interface might define a property with only a get accessor while the implementing type also has a set accessor (see Example 2-42).

Example 2-42. Adding a set accessor to an implemented interface property

interface IReadOnlyInterface

{

int Value { get; }

}

struct ReadAndWriteImplementation:IReadOnlyInterface

{

public int Value { get; set; }

}

In this case, the implementing class adds an extra set accessor. The advantage of using this pattern is that if a user accesses your class through its interface, it will see only the get accessor. Direct users of the class will see both the get and the set accessor.

Interfaces can also inherit from other interfaces. This way, you can have a chain of interfaces that each adds to the public signature of a type. A class that inherits from one of the derived interfaces has to implement all signatures in the whole hierarchy.

You can also use generics when defining interfaces. For example, if you are implementing the repository pattern (a repository offers access to objects stored in a database or some other storage type), you can use a generic type parameter so you don’t have to create different interfaces for each entity that you want to store, as Example 2-43 shows.

Example 2-43. Creating an interface with a generic type parameter

interface IRepository<T>

{

T FindById(int id);

IEnumerable<T> All();

}

Now you can create concrete implementations of your Repository<T> for different classes. For example, you can have an IRepository<Product> and an IRepository<Order>. Generic interfaces can have multiple type parameters and type constraints.

Using interfaces

Because an interface has no implementation, you can’t instantiate an interface directly. You can instantiate only a concrete type that implements an interface, as Example 2-44 shows.

Example 2-44. Instantiating a concrete type that implements an interface

interface IAnimal

{

void Move();

}

class Dog : IAnimal

{

public void Move() {}

public void Bark() {}

}

IAnimal animal = new Dog();

Your code now holds a reference to some implementation of the interface IAnimal. Although you know that it actually points to a Dog, you can’t call the method Bark on it. If you want to go from the interface IAnimal to the type Dog, you will have to cast it.

MORE INFO: CASTING AND CONVERTING

For more information on casting an interface to another type, see Objective 2.2: Consume types later in this chapter.

Interfaces can also be used as parameters in a method. This way you can create generic code that can work against all kinds of different implementations:

void MoveAnimimal(IAnimal animal)

{

animal.Move();

}

One of the important concepts of object-oriented development is programming against a contract, not an implementation. The interface guarantees you that certain functionality is available (the contract). You shouldn’t care how this is implemented, only that it works. This helps with writing code that’s loosely coupled and can be better maintained.

NOTE: NO MULTIPLE INHERITANCE

Some languages such as C++ offer the concept of multiple inheritance. This means that a single class can have multiple base classes. This way, a Bat can be both a Mammal (which inherits from Animal) and a DrawableObject. Multiple inheritance is not supported in C#. The creators of C# decided against implementing multiple inheritance because of the associated difficulties it can have. When using multiple inheritance, you can get conflicts when both base classes have a method with the same signature. C# does offer multiple interface inheritance with the option of explicitly implementing an interface to separate the different implementations. Multiple class inheritance is not supported.

Creating and using base classes

An interface defines only the public signature of a type. Deriving from an interface doesn’t inherit any implementation code. The derived type is completely free in how to implement the interface. When you do want to inherit implementation code, you can inherit from another class. Example 2-45 shows how to create a base class.

Example 2-45. Creating a base class

interface IEntity

{

int Id { get; }

}

class Repository<T>

where T : IEntity

{

protected IEnumerable<T> _elements;

public Repository(IEnumerable<T> elements)

{

_elements = elements;

}

public T FindById(int id)

{

return _elements.SingleOrDefault(e => e.Id == id);

}

}

The Repository base class offers a method for finding entities by ID. This code is generic and can be used by all entities. What if you want to add a specific query that would filter orders on date and amount? That wouldn’t be something that applied to all entities; only to the order entity. Using inheritance can help you reuse your code while adding some extra behavior. Example 2-46 shows how to inherit from a class.

Example 2-46. Inheriting from a base class

class Order : IEntity

{

public int Id { get; }

// Other implementation details omitted

// ...

}

class OrderRepository : Repository<Order>

{

public OrderRepository(IEnumerable<Order> orders)

: base(orders) { }

public IEnumerable<Order> FilterOrdersOnAmount(decimal amount)

{

List<Order> result = null;

// Some filtering code

return result;

}

}

The OrderRepository now has both a method for finding an order by ID and a specific method for filtering orders on their amount. You can use inheritance in a similar manner to add members to an existing type. As you can see, you can use the base keyword to call the constructor of the base class. The base keyword can also be used when you want to call methods or other members on a base class.

NOTE: CHILD AND PARENT OR BASE AND DERIVED

When talking about inheritance, the terms parent and child classes are often used. But is that the correct terminology when thinking about inheritance? For example, a Dog is a kind of Animal. This can be modeled by using an inheritance relation. However, would you say that a Dog is a Child of an Animal? A Child is not a kind of Parent. In languages that support multiple inheritance things get even messier. Instead of using Parent and Child when defining an inheritance relation, you can better use the terms base and derived class to avoid any confusion with inheritance in the real world.

Changing behavior

When building a class hierarchy, you sometimes want to replace or extend the behavior of a base class. Assume that you want to add some logging capabilities to the repository you created. You don’t want to rewrite all filtering code; instead you just want to add some extra behavior.

This is where the virtual and override keywords come into play. Marking a method virtual allows derived classes to override the method. The derived class can choose to completely replace or to extend the behavior of the base class. Example 2-47 shows how to override a method to extend the base class.

Example 2-47. Overriding a virtual method

class Base

{

protected virtual void Execute()

{}

}

class Derived : Base

{

protected override void Execute()

{

Log("Before executing");

base.Execute();

Log("After executing");

}

private void Log(string message) { /* some logging code */ }

}

By marking the method in the base class as virtual, the derived class can override it. By prefixing a method name with base, a derived class can execute the method on the base class. By skipping the call to base, the derived class completely replaces the functionality.

If a base class doesn’t declare a method as virtual, a derived class can’t override the method. It can, however, use the new keyword, which explicitly hides the member from a base class (this is different from using the new keyword to create a new instance of an object). This can cause some tricky situations, as Example 2-48 shows.

Example 2-48. Hiding a method with the new keyword

class Base

{

public void Execute() { Console.WriteLine("Base.Execute"); }

}

class Derived : Base

{

public new void Execute() { Console.WriteLine("Derived.Execute"); }

}

class Program

{

static void Main(string[] args)

{

Base b = new Base();

b.Execute();

b = new Derived();

b.Execute();

}

}

Running this code will output Base.Execute twice. If you change the base execute method to be virtual and the derived class to override instead of hide the Execute method, the code will display Base.Execute and Derived.Execute. You should try to avoid hiding methods with the newkeyword.

Abstract and sealed base classes

If you don’t want to allow a base class to be instantiated, you can declare it as an abstract class. An abstract class can have implementation code for its members, but it’s not required. Because the class is abstract, you can’t use the new operator on it to create a new instance. Example 2-49 shows how to declare an abstract class.

Example 2-49. Creating an abstract class

abstract class Base

{

public virtual void MethodWithImplementation() {/*Method with implementation*/}

public abstract void AbstractMethod();

}

class Derived : Base

{

public override void AbstractMethod() { }

}

As you can see, an abstract class can have both fully implemented members and abstract members. A concrete derived type is required to implement all abstract members (just as with an interface). Abstract classes can be a nice way to share both an interface and some implementation details, especially when only derived types should be instantiable.

Example 2-49 uses the override keyword to implement the abstract method that’s defined in the base class. It can also be used on abstract or virtual methods, properties, indexers, and events to extend or modify the implementation.

The opposite of an abstract class is a sealed class, which cannot be derived from. As such, it can’t be marked as abstract, and all members should have an implementation. Structs are implicitly sealed in C#. It’s never possible to inherit from a struct. Marking a class as sealed is a good practice. If you don’t do this, others can start inheriting from your class without you having thought about this. If inheritance is necessary, you can remove the sealed keyword and think about the implications.

EXAM TIP

Make sure that you know the difference between an interface and an abstract class. An interface has no implementation code. An abstract class can choose to implement methods or leave it to the derived class.

Liskov substitution principle

Inheritance is a powerful technique, but it should be used with caution. As already mentioned, inheritance should be used only when you are dealing with a “is-a-kind-of” relationship. The Liskov substitution principle states that a subclass should be usable in each place you can use one of the base classes. They shouldn’t suddenly change behavior that users would depend on.

It’s easy to violate this principle. Consider the code in Example 2-50.

Example 2-50. A Rectangle class with an Area calculation

class Rectangle

{

public Rectangle(int width, int height)

{

Width = width;

Height = height;

}

public int Height { get; set; }

public int Width { get; set; }

public int Area

{

get

{

return Height * Width;

}

}

}

When looking at this Rectangle class, would you say that a Square is a kind of Rectangle? In mathematics, this would be true. We know that a square is a special type of rectangle. You can model this using an inheritance relation, as shown in Example 2-51.

Example 2-51. A Square class that inherits from Rectangle

class Square : Rectangle

{

public override int Width

{

get

{

return base.Width;

}

set

{

base.Width = value;

base.Height= value;

}

}

public override int Height

{

get

{

return base.Height;

}

set

{

base.Height = value;

base.Width = value;

}

}

}

Because you know that you are dealing with a square, you help the user of the class by modifying both the Width and Height properties together. This way, the rectangle will always be a square.

Suppose you want to use the class as shown in Example 2-52.

Example 2-52. Using the Square class

Rectangle rectangle = new Square();

rectangle.Width = 10;

rectangle.Height = 5;

Console.WriteLine(rectangle.Area);

This code will output 25. The user thinks he’s dealing with a Rectangle with a calculated Area, but because the Rectangle is pointing to a Square, only the latest value of Height is stored.

This is a typical example of violating the Liskov substitution principle. The Square class cannot be used in all places where you would normally use a Rectangle.

Implementing standard .NET Framework interfaces

The .NET Framework has a few standard interfaces that can you can use on your own types. When implementing those interfaces, your classes can be used in the infrastructure that the .NET Framework offers.

IComparable

The IComparable interface features a single method, as shown in Example 2-53.

Example 2-53. IComparable interface

public interface IComparable

{

int CompareTo(object obj);

}

This interface is used to sort elements. The CompareTo method returns an int value that shows how two elements are related. Table 2-5 shows the possible values the CompareTo method returns.

Table 2-5. Return values of CompareTo

Value

Meaning

Less than zero

The current instance precedes the object specified by the CompareTo method in the sort order.

Zero

This current instance occurs in the same position in the sort order as the object specified by the CompareTo method.

Greater than zero

This current instance follows the object specified by the CompareTo method in the sort order.

For example, if you are creating an Order class that has a DateTime Created property that you want to sort on, you can implement IComparable on the Order class and compare the Created dates of both orders. Example 2-54 shows how to do this.

Example 2-54. Implementing the IComparable interface

class Order : IComparable

{

public DateTime Created { get; set; }

public int CompareTo(object obj)

{

if (obj == null) return 1;

Order o = obj as Order;

if (o == null)

{

throw new ArgumentException("Object is not an Order");

}

return this.Created.CompareTo(o.Created);

}

}

List<Order> orders = new List<Order>

{

new Order { Created = new DateTime(2012, 12, 1 )},

new Order { Created = new DateTime(2012, 1, 6 )},

new Order { Created = new DateTime(2012, 7, 8 )},

new Order { Created = new DateTime(2012, 2, 20 )},

};

orders.Sort();

The call to orders.Sort() calls the CompareTo method to sort the items. After sorting, the list contains the ordered Orders.

IComparable also has a generic version: IComparable<T>. Especially when dealing with methods from the .NET Framework, it’s a good idea to implement both IComparable and IComparable<T>. Of course, you can share some code between those two implementations.

IEnumerable

The IEnumerable and IEnumerator interface in .NET helps you to implement the iterator pattern, which enables you to access all elements in a collection without caring about how it’s exactly implemented. You can find these interfaces in the System.Collection andSystem.Collections.Generic namespaces. When using the iterator pattern, you can just as easily iterate over the elements in an array, a list, or a custom collection. It is heavily used in LINQ, which can access all kinds of collections in a generic way without actually caring about the type of collection.

The IEnumerable interface exposes a GetEnumerator method that returns an enumerator. The enumerator has a MoveNext method that returns the next item in the collection.

The foreach statement in C# is some nice syntactic sugar that hides from you that you are using the GetEnumerator and MoveNext methods. Example 2-55 shows how to iterate over a collection without using foreach.

Example 2-55. Syntactic sugar of the foreach statement

List<int> numbers = new List<int> { 1, 2, 3, 5, 7, 9 };

using (List<int>.Enumerator enumerator = numbers.GetEnumerator())

{

while (enumerator.MoveNext()) Console.WriteLine(enumerator.Current);

}

The GetEnumerator function on an IEnumerable returns an IEnumerator. You can think of this in the way it’s used on a database: IEnumerable<T> is your table and IEnumerator is a cursor that keeps track of where you are in the table. It can only move to the next row. You can have multiple database cursors around that all keep track of their own state.

Before C# 2 implementing IEnumerable on your own types was quite a hassle. You need to keep track of the current state and implement other functionality such as checking whether the collection was modified while you were enumerating over it. C# 2 made this a lot easier, as Example 2-56 shows.

Example 2-56. Implementing IEnumerable<T> on a custom type

class Person

{

public Person(string firstName, string lastName)

{

FirstName = firstName;

LastName = lastName;

}

public string FirstName { get; set; }

public string LastName { get; set; }

public override string ToString()

{

return FirstName + " " + LastName;

}

}

class People : IEnumerable<Person>

{

public People(Person[] people)

{

this.people = people;

}

Person[] people;

public IEnumerator<Person> GetEnumerator()

{

for (int index = 0; index < people.Length; index++)

{

yield return people[index];

}

}

IEnumerator IEnumerable.GetEnumerator()

{

return GetEnumerator();

}

}

Notice the yield return in the GetEnumerator function. Yield is a special keyword that can be used only in the context of iterators. It instructs the compiler to convert this regular code to a state machine. The generated code keeps track of where you are in the collection and it implements methods such as MoveNext and Current.

Because creating iterators is so easy now, it has suddenly become a feature that you can use in your own code quite easily. Whenever you do a lot of manual loops through the same data structure, think about the iterator pattern and how it can help you create way nicer code.

IDisposable

Another useful interface in the .NET Framework is IDisposable. This interface is used to facilitate working with external, unmanaged resources. As Objective 2.6 discusses, C# is a managed language that uses a garbage collector to clean up memory. However, you will still access external, unmanaged resources like database connections or file handles. This is where IDisposable comes into play. Example 2-57 shows the definition of the IDisposable interface.

Example 2-57. The IDisposable interface

public interface IDisposable

{

void Dispose();

}

The only method the IDisposable interface has is Dispose(). This method is used to free any unmanaged resources.

MORE INFO: IMPLEMENTING IDISPOSABLE

For more information implementing IDisposable, see Objective 2.6: Manage the object life cycle later in this chapter.

IUnknown

Before .NET existed, the first generation of the Windows API was based on a library of functions contained in a dynamic-link library (DLL). Later generations collected these functions into a Component Object Model (COM) interface. The .NET Framework provides classes that wrap much of these APIs in a managed version so that in normal life you almost never touch any COM components directly.

Normally, you just add a reference to a COM object and the compiler generates the necessary wrapper classes called COM Interop classes. If this fails for some reason, you have to create the wrapper class; this is where the IUnknown interface is used.

MORE INFO: IMPLEMENTING IUNKNOWN

For more information about implementing IUnknown, see http://msdn.microsoft.com/en-us/library/aa645712(v=vs.71).aspx.

THOUGHT EXPERIMENT

Optimizing your code

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are working on a brand-new web application for a real estate agent. The agent wants to display his property on a website and ensure that users can easily search for it. For example, a user will be able to filter the results on location, size, property type, and price. You need to create the infrastructure that uses all the selected criteria to filter the list of available houses.

You want to see whether you can use some of the standard interfaces from the .NET Framework to implement your infrastructure.

1. Why does the .NET Framework offer some interfaces without any implementation? Wouldn’t it be easier if the .NET Framework used abstract base classes?

2. Would you use interface or class inheritance to create your search criteria?

3. Which of the following interfaces would you use?

§ IComparable

§ IEnumerable

§ IDisposable

§ IUnknown

Objective summary

§ Inheritance is the process in which a class is derived from another class or from an interface.

§ An interface specifies the public elements that a type must implement.

§ A class can implement multiple interfaces.

§ A base class can mark methods as virtual; a derived class can then override those methods to add or replace behavior.

§ A class can be marked as abstract so it can’t be instantiated and can function only as a base class.

§ A class can be marked as sealed so it can’t be inherited.

§ The .NET Framework offers default interfaces such as IComparable, IEnumerable, IDisposable and IUnknown.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to create a hierarchy of types because you have some implementation code you want to share between all types. You also have some method signatures you want to share. What should you use?

a. An interface

b. A class with virtual methods

c. An abstract class

d. A sealed class

2. You want to create a type that can be easily sorted. Which interface should you implement?

a. IEnumerable

b. IComparable

c. IDisposable

d. IUnknown

3. You want to inherit from an existing class and add some behavior to a method. Which steps do you have to take? (Choose all that apply.)

a. Use the abstract keyword on the base type.

b. Use the virtual keyword on the base method.

c. Use the new keyword on the derived method.

d. Use the override keyword on the derived method.

Objective 2.5: Find, execute, and create types at runtime by using reflection

A .NET application doesn’t just contain code and data; it also contains metadata, which is information about data. In .NET, this means that an application contains the code that defines the application and data that describes the code. An attribute is one type of metadata that can be stored in a .NET application. Other types of metadata contain information about the types, code, assembly, and all other elements stored in your application. Reflection is the process of retrieving this metadata at runtime. The data can be inspected and used to make decisions. In this section, you will learn how to use attributes in your own code and how to use reflection. You will also look at generating code at runtime by using both CodeDom and expression trees.

THIS OBJECTIVE COVERS HOW TO:

§ Create and use attributes.

§ Use reflection to inspect and execute code at runtime.

§ Generate code at runtime.

Creating and using attributes

Using attributes is a powerful way to add metadata to an application. Attributes can be added to all kinds of types: assemblies, types, methods, parameters, and properties. At runtime, you can query for the existence of an attribute and its settings and then take appropriate action.

Attributes are used for a variety of reasons. They can be used to describe the author information of an assembly or to give specific hints to the compiler on how to optimize your code. Custom attributes can store all types of data that you want.

Applying attributes

In C#, you apply an attribute by placing the attribute name in square brackets ([]) above the declaration that you want the attribute to apply to.

One example of an attribute in the .NET Framework is System.SerializableAttribute. This attribute indicates that a type can be serialized. The .NET Framework checks for the existence of this attribute when serializing a type, and it makes sure that all members of the type can also be serialized. Example 2-58 shows how to apply the Serializable attribute.

Example 2-58. Applying an attribute

[Serializable]

class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

}

As you can see, the actual class in the .NET Framework is called SerializableAttribute. By convention, the name is suffixed with Attribute so you can easily distinguish between attributes and other types in the .NET Framework. When using the attribute, however, you can skip the Attributesuffix.

A type can have as many attributes applied to it as necessary. Some attributes can even be applied multiple times. For example, you can use the ConditionalAttribute to indicate to the compiler that a method call should be ignored unless a specific compiler option is specified. Example 2-59 shows how to apply this attribute.

Example 2-59. Using multiple attributes

[Conditional("CONDITION1"), Conditional("CONDITION2")]

static void MyMethod(){ }

As shown in the listing, an attribute can have parameters. Just as with regular types, those parameters can be named an optional. The values set to an attribute can later be inspected at runtime.

An attribute also has a specific target to which it applies. It can be an attribute applied to a whole assembly, a class, a specific method, or even a parameter of a method.

If you look at the AssemblyInfo.cs of a new class library, you can see how the target is explicitly specified (see Example 2-60).

Example 2-60. Specifying the target of an attribute explicitly

[assembly: AssemblyTitle("ClassLibrary1")]

[assembly: AssemblyDescription("")]

[assembly: AssemblyConfiguration("")]

[assembly: AssemblyCompany("")]

[assembly: AssemblyProduct("ClassLibrary1")]

[assembly: AssemblyCopyright("Copyright © 2013")]

[assembly: AssemblyTrademark("")]

[assembly: AssemblyCulture("")]

These attributes are all applied to the current assembly and describe some metadata about the assembly.

Reading attributes

Applying an attribute isn’t that useful if you can’t retrieve it. Luckily, the .NET Framework offers support for reading attributes through a process called reflection. The System.Attribute class, from which all other attributes inherit, defines some static methods that can be used to see whether an attribute is applied and to get the current instance of an attribute so you can further inspect it.

Suppose that you want to check that a class has the Serializable attribute applied. You can do this by calling the static IsDefined method on Attribute, as Example 2-61 shows.

Example 2-61. Seeing whether an attribute is defined

[Serializable]

class Person { }

if (Attribute.IsDefined(typeof(Person), typeof(SerializableAttribute))) { }

You can also retrieve the specific instance of an attribute so that you can look at its properties. Example 2-62 shows how you can get the ConditionalAttribute from Example 2-59.

Example 2-62. Getting a specific attribute instance

ConditionalAttribute conditionalAttribute =

(ConditionalAttribute)Attribute.GetCustomAttribute(

typeof(ConditionalClass),

typeof(ConditionalAttribute));

string condition = conditionalAttribute.ConditionString; // returns CONDITION1

The GetAttribute and GetAttributes methods have several overloads so you can inspect attributes for an assembly, method, module, or a parameter.

Creating custom attributes

Next to the built-in attributes of the .NET Framework, you can also create your own attributes. A custom attribute class has to derive from System.Attribute (directly or indirectly). For example, xUnit (a popular unit testing framework) enables you to categorize your unit tests by applying an attribute to them.

Example 2-63. Using a category attribute in xUnit

[Fact]

[Trait("Category", "Unit Test")]

public void MyUnitTest()

{ }

[Fact]

[Trait("Category", "Integration Test")]

public void MyIntegrationTest()

{ }

Using the Trait attribute works perfect except that that you have to type the Category and Value by hand each time. This is error-prone and repetitive work that you want to avoid as much as possible. Luckily, xUnit gives you the option to create your own custom attributes that inherit fromTrait. Example 2-64 shows how to create a derived attribute that you can use more easily.

Example 2-64. Creating a custom attribute

public class CategoryAttribute : TraitAttribute

{

public CategoryAttribute(string value)

: base("Category", value)

{ }

}

public class UnitTestAttribute : CategoryAttribute

{

public UnitTestAttribute()

: base("Unit Test")

{ }

}

As you can see in Example 2-65, the new UnitTestAttribute can be easily applied to methods and mark them as being in the Unit Test category.

Example 2-65. Using a custom attribute

[Fact]

[UnitTest]

public void MySecondUnitTest()

{}

When creating your own custom attribute from scratch, you also have to define the targets on which an attribute can be used. For example, you may want your attribute to be used only on methods and parameters. Example 2-66 shows how to set the allowed targets of your custom attribute.

Example 2-66. Defining the targets for a custom attribute

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]

public class MyMethodAndParameterAttribute : Attribute { }

Defining the usage of an attribute is done by applying an attribute. You can combine as many targets as you want. You can also use the AllowMultiple parameter to enable multiple instances of one attribute to a single type (see Example 2-67).

Example 2-67. Setting the AllowMultiple parameter for a custom attribute

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]

class MyMultipleUsageAttribute : Attribute{ }

After having declared your custom attribute, you can add properties to it and a constructor to initialize your attribute. Be aware, however, that attributes require all properties to be read-write. Example 2-68 shows how to add a custom property.

Example 2-68. Adding properties to a custom attribute

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)]

class CompleteCustomAttribute : Attribute

{

public CompleteCustomAttribute(string description)

{

Description = description;

}

public string Description { get; set; }

}

Using reflection

When reading attributes, you’ve already looked at some of the functionality that reflection offers. Reflection enables an application to collect information about itself and act on this information. Reflection is slower than normally executing static code. It can, however, give you a flexibility that static code can’t provide.

The most basic example of reflection is getting the current type of an object you have:

int i = 42;

System.Type type = i.GetType();

This returns System.Int32 as the type of int. System.Type is a class in the .NET Framework that you can use to get all kinds of metadata about any given type.

When would you use reflection? Assume that you want to create a plug-in system, and you have a directory in your system that contains all plug-ins. If a new assembly is dropped in this location, you inspect the assembly for the plug-ins it contains and then add them to your application. This is impossible without reflection.

MORE INFO: MANAGED EXTENSIBILITY FRAMEWORK

If you are looking into building such a plug-in system, have a look at the Managed Extensibility Framework (MEF) at http://msdn.microsoft.com/en-us/library/dd460648.aspx.

When creating a plug-in system, you need some way of finding plug-ins, getting some info, and executing them. One option is to create a custom IPlugin interface that exposes members that give you information about the plug-in and the capability to load it (see Example 2-69).

Example 2-69. Creating an interface that can be found through reflection

public interface IPlugin

{

string Name { get; }

string Description { get; }

bool Load(MyApplication application);

}

Now that you have a custom base interface, you can create a plug-in by inheriting from this interface with a specific plug-in class, as Example 2-70 shows.

Example 2-70. Creating a custom plug-in class

public class MyPlugin : IPlugin

{

public string Name

{

get { return "MyPlugin"; }

}

public string Description

{

get { return "My Sample Plugin"; }

}

public bool Load(MyApplication application)

{

return true;

}

}

Using reflection, you can now inspect an assembly and check it for any available plug-ins. The types you get back can then be used to create an instance of the plug-in and use it. The System.Reflection namespace defines the elements you need for reflection.

Example 2-71 shows how to get all plug-ins from an assembly with a LINQ query and construct them.

Example 2-71. Inspecting an assembly for types that implement a custom interface

Assembly pluginAssembly = Assembly.Load("assemblyname");

var plugins = from type in pluginAssembly.GetTypes()

where typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface

select type;

foreach (Type pluginType in plugins)

{

IPlugin plugin = Activator.CreateInstance(pluginType) as IPlugin;

}

The first line loads the assembly by name. One thing to note is that if you call this multiple times, the runtime will load the assembly only once. If you want to reload the assembly you would have to restart your application. After you have the assembly, you can check which plugins are defined in it and then construct your IPlugin objects.

Reflection can also be used to inspect the value of a property or a field. Suppose that you need to create a method that iterates over an object and selects all the private integer fields to display them on-screen. You can easily do this by using reflection (see Example 2-72). You use theBindingFlags enumeration to control how reflection searches for members.

Example 2-72. Getting the value of a field through reflection

static void DumpObject(object obj)

{

FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);

foreach (FieldInfo field in fields)

{

if (field.FieldType == typeof(int))

{

Console.WriteLine(field.GetValue(obj));

}

}

}

Reflection can also be used to execute a method on a type. You can specify the parameters the method needs, and at runtime the .NET Framework will see whether the parameters match and it will execute the method (see Example 2-73).

Example 2-73. Executing a method through reflection

int i = 42;

MethodInfo compareToMethod = i.GetType().GetMethod("CompareTo",

new Type[] { typeof(int) });

int result = (int)compareToMethod.Invoke(i, new object[] { 41 });

Using CodeDom and lambda expressions to generate code

Besides inspecting types at runtime through reflection, C# also has support for generating code at runtime. One way this is done is through the CodeDOM. You can use the CodeDOM to create an object graph that can be converted to a source file or to a binary assembly that can be executed.

Typical usage scenarios for using the CodeDOM involve generating code for ASP.NET, Web Services, code wizards, or designers. Every time you create the same code over and over with some slight modifications, you can look into the CodeDOM to automate the process. The nice thing about the CodeDOM is that you can represent the logical structure of a piece of code independent of the specific language syntax you use. For example, you can use the CodeDOM to create a source file in both Visual Basic and C# syntax.

The CodeDOM is located in the System.CodeDom namespace. You can think of your source file as a tree with containers. You have a topmost container (called a CodeCompileUnit) that contains other elements such as namespaces, classes, methods, and individual statements. If you want to output a simple Hello World application, you need to create a CodeCompileUnit, a namespace, a class, and the entry Main method of your program that will call Console.WriteLine (see Example 2-74).

Example 2-74. Generating “Hello World!” with the CodeDOM

CodeCompileUnit compileUnit = new CodeCompileUnit();

CodeNamespace myNamespace= new CodeNamespace("MyNamespace");

myNamespace.Imports.Add(new CodeNamespaceImport("System"));

CodeTypeDeclaration myClass = new CodeTypeDeclaration("MyClass");

CodeEntryPointMethod start = new CodeEntryPointMethod();

CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(

new CodeTypeReferenceExpression("Console"),

"WriteLine", new CodePrimitiveExpression("Hello World!"));

compileUnit.Namespaces.Add(myNamespace);

myNamespace.Types.Add(myClass);

myClass.Members.Add(start);

start.Statements.Add(cs1);

Now that the compilation unit is complete, you can create a source file from it with the CSharpCodeProvider class that you can find in the Microsoft.CSharp namespace (see Example 2-75).

Example 2-75. Generating a source file from a CodeCompileUnit

CSharpCodeProvider provider = new CSharpCodeProvider();

using (StreamWriter sw = new StreamWriter("HelloWorld.cs", false))

{

IndentedTextWriter tw = new IndentedTextWriter(sw, " ");

provider.GenerateCodeFromCompileUnit(compileUnit, tw,

new CodeGeneratorOptions());

tw.Close();

}

The generated output in HelloWorld.cs is shown in Example 2-76.

Example 2-76. The automatically generated source file

//------------------------------------------------------------------------------

// <auto-generated>

// This code was generated by a tool.

// Runtime Version:4.0.30319.18010

//

// Changes to this file may cause incorrect behavior and will be lost if

// the code is regenerated.

// </auto-generated>

//------------------------------------------------------------------------------

namespace MyNamespace {

using System;

public class MyClass {

public static void Main() {

Console.WriteLine("Hello World!");

}

}

}

Lambda expressions

Lambda functions were introduced in C# 3.0. You can think of a lambda expression as a compact method to create an anonymous method.

MORE INFO: ANONYMOUS METHODS

For more info on anonymous methods, see http://msdn.microsoft.com/en-us/library/0yw3tz5k.aspx.

When working with lambdas, you will also need to know about the Func<..> and Action types. These generic types were added to have some predefined delegate types in the .NET Framework. You use Action when you have a delegate that doesn’t return a value and Func when you do want to return a value. Both can take up to 16 type arguments in the .NET Framework 4.0.

When combining lambda and the Func type, you can easily create a type that returns the sum of two integers, as shown in Example 2-77.

Example 2-77. Creating a Func type with a lambda

Func<int, int, int> addFunc = (x, y) => x + y;

Console.WriteLine(addFunc(2, 3));

The lambda is of the type Func<int, int, int> which means that it takes two integer arguments and returns an int as result. The strange => notation can be read as “becomes” or “for which.” The addFunc type can be read as “x, y become x + y”.

Expression trees

When using lambdas, you will come across expression trees, which are representations of code in a tree-like data structure. Just as the CodeDom can represent code in a tree-like manner, Expression trees can do the same; they can also be used to generate code.

An expression tree describes code instead of being the code itself. Expression trees are heavily used in LINQ. When using Linq To Entities to query a database, the query is not executed (as is the case in Linq To Objects). Instead, an expression tree describes the query. Later on, this expression tree is translated in a SQL statement that can be sent to the database.

The System.Linq.Expressions namespace contains all the types you need to create an expression. You have expressions for calling a method and creating a new object or even basic operations such as addition or subtraction.

The Hello World example from the CodeDOM can also be created as an expression tree (see Example 2-78).

Example 2-78. Creating “Hello World!” with an expression tree

BlockExpression blockExpr = Expression.Block(

Expression.Call(

null,

typeof(Console).GetMethod("Write", new Type[] { typeof(String) }),

Expression.Constant("Hello ")

),

Expression.Call(

null,

typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }),

Expression.Constant("World!")

)

);

Expression.Lambda<Action>(blockExpr).Compile()();

The expression is first constructed with a call to Console.Write and Console.WriteLine. After construction, the expression is compiled to an Action (because it doesn’t return anything) and executed.

MORE INFO: EXPRESSION TREES

For more info on expression trees, see http://msdn.microsoft.com/en-us/library/bb397951.aspx.

THOUGHT EXPERIMENT

Optimizing your code

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are creating your own optimized object-relational mapper. You allow the user to map types one-on-one to a table in the database. You also use special attributes for security reasons. For example, a type can be decorated with an AuthorizeAttribute to make sure that only specific users can access a certain table. You use a lot of reflection in your app and you start seeing some performance problems. You are also thinking about a generator that will create types that map exactly to an existing database.

1. Why do you use an attribute instead of inheriting from an interface? Wouldn’t that be easier than adding a whole new concept to C#?

2. What can you do about the performance problems with using reflection?

3. Which technique would you use to create your generator?

Objective summary

§ A C# assembly stores both code and metadata.

§ Attributes are a type of metadata that can be applied in code and queried at runtime.

§ Reflection is the process of inspecting the metadata of a C# application.

§ Through reflection you can create types, call methods, read properties, and so forth.

§ The CodeDOM can be used to create a compilation unit at runtime. It can be compiled or converted to a source file.

§ Expression trees describe a piece of code. They can be translated to something else (for example, SQL) or they can be compiled and executed.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to read the value of a private field on a class. Which BindingFlags do you need? (Choose all that apply.)

a. Instance

b. DeclaredOnly

c. Static

d. NonPublic

2. You need to create an attribute that can be applied multiple times on a method or a parameter. Which syntax should you use?

a. [AttributeUsage(AttributeTargets.GenericParameter | AttributeTargets.Method,AllowMultiple = true)]

b. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)]

c. [AttributeUsage(AttributeTargets.All)]

d. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)]

3. You want to create a delegate that can filter a list of strings on a specific value. Which type should you use?

a. Action<bool, IEnumerable<string>>.

b. Func<IEnumerable<string>, IEnumerable<string>>.

c. Func<string, IEnumerable<string>, IEnumerable<string>>.

d. Func<IEnumerable<string>>.

Objective 2.6: Manage the object life cycle

In languages such as C++, you have to worry about memory management. Using pointers and managing memory whenever possible is necessary for writing applications. Forgetting to free some memory can result in a memory leak. In C#, things are different. C# is a managed language that uses a garbage collector to free memory whenever necessary. As long as you use only managed objects, the garbage collector frees you from worrying about memory management. However, when writing applications, you will often cross the boundaries and use unmanaged resources such as database connections or file handles. When dealing with these kinds of situations, you need to free those resources as soon as possible. In this objective, you learn how you can work with unmanaged resources and how you can influence the garbage collection when you need it.

THIS OBJECTIVE COVERS HOW TO:

§ Manage unmanaged resources.

§ Implement and use IDisposable.

§ Manage finalization and garbage collection.

Understanding garbage collection

When building an application in C#, you rely on the fact that C# manages all memory for you. When you create a new object—whether a string, an int, or a custom type—you know that somehow the .NET Framework allocates memory and puts your object in it. Most of the time, this process happens completely behind the scenes (by the CLR), and you don’t ever have to know about it. But having a basic understanding of how things work helps you understand why things are happening and enables you to optimize your code when it’s necessary.

There are two places in memory where the CLR stores items while your code executes. One is the stack; the other is the heap. The stack keeps track of what’s executing in your code, and the heap keeps track of your objects.

Of course, this is an oversimplification. Value types can be stored on both the stack and the heap. For an object on the heap, there is always a reference on the stack that points to it. Large objects go on a special part of the heap. But basically, this is the distinction between the two types of memory.

The stack is automatically cleared at the end of a method. The CLR takes care of this and you don’t have to worry about it.

The heap is another story—it is managed by the garbage collector. In unmanaged environments without a garbage collector, you have to keep track of which objects were allocated on the heap and you need to free them explicitly. In the .NET Framework, this is done by the garbage collector.

The garbage collector works with a mark and compact algorithm. The mark phase of a collection checks which items on the heap are still being referenced by a root item. A root can be a static field, a method parameter, a local variable, or a CPU register. If the garbage collector finds a “living” item on the heap, it marks the item. After checking the whole heap, the compact operation starts. The garbage collector then moves all living heap objects close together and frees the memory for all other objects.

For doing this, the garbage collector has to make sure that no state is changing while performing all the marking and compacting. Because of this, all threads are frozen while doing a collect operation. It also has to make sure that all references to living objects are correct. After moving objects around, the garbage collector will fix all existing references to objects.

This can have a huge performance impact. When you are executing a complex important operation that should return to the user as fast as possible and suddenly the garbage collector kicks in, you have to wait till it finishes before you can continue.

But luckily for us, the garbage collector is quite clever. The garbage collector starts cleaning up only when there is not enough room on the heap to construct a new object (or when Windows signals that it’s low on memory). So as long as there is enough room in memory, you won’t ever notice anything about garbage collection. And when it does, it tries to do this on a moment that the usage of the application is low.

When garbage collection does start, it collects only Generation 0. What does this mean? Well, when executing a cleanup, items that survive (because they are still being referenced) are promoted to a higher generation. They are longer-living objects and the garbage collector makes the assumption that longer-living objects will probably stay around for some time. Because of this, the garbage collector focuses on the objects in Generation 0. They are just created and will probably be unnecessary in a small amount of time. The other generations will be touched only when the garbage collector cannot free enough memory by cleaning Generation 0.

In short, that’s how garbage collection works: It removes items from the heap that are no longer necessary and makes sure that no object can stay alive and occupy memory if it’s not in use.

Managing unmanaged resources

All of this would be enough if you were only working with managed resources. Keeping objects such as strings, numbers, and other managed types around is completely handled by the garbage collector. But when you start accessing unmanaged resources, things change.

Unmanaged resources can be a network connection, file handle, window handle, and so on. You have to explicitly release those items. If not, you will get errors such as “This File is in use” or you won’t be able to connect to your database because all connections are in use.

Because of this, C# supports the concept of finalization. This mechanism allows a type to clean up prior to garbage collection. It’s important to understand that a C# finalizer is not the same as a C++ destructor. C++ destructors can be called deterministic. You know when they will execute. In C#, however, you can’t be sure when a finalizer is called. It will happen only when the garbage collector determines that your object is ready for being cleaned up.

A finalizer in C# requires some special syntax, just as a constructor. You need to prefix the class name with a tilde (~) to create a finalizer. Example 2-79 shows how to declare a finalizer.

Example 2-79. Adding a finalizer

public class SomeType

{

~SomeType()

{

// This code is called when the finalize method executes

}

}

Inside the finalizer, you can clean up other resources and make sure that all memory is freed. For example, when working with a File inside C# you will have to free any unmanaged resources before you can delete the file, as Example 2-80 shows.

Example 2-80. Not closing a file will throw an error

StreamWriter stream = File.CreateText("temp.dat");

stream.Write("some data");

File.Delete("temp.dat"); // Throws an IOException because the file is already open.

As already mentioned, the finalizer is called only when a garbage collection occurs. You can force this by adding a call to GC.Collect, as shown in Example 2-81. The line WaitForPendingFinalizers makes sure that all finalizers have run before the code continues. The garbage collector is pretty smart in managing memory, and it’s not recommended that you call GC.Collect yourself.

Example 2-81. Forcing a garbage collection

StreamWriter stream = File.CreateText("temp.dat");

stream.Write("some data");

GC.Collect();

GC.WaitForPendingFinalizers();

File.Delete("temp.dat");

When running this piece of code in Release mode, the garbage collector will see that there are no more references to stream, and it will free any memory associated with the Stream-Writer instance. This will run the finalizer, which in turn will release any file handles to the temp.dat file (in debug mode, the compiler will make sure that the reference isn’t garbage collected till the end of the method).

What’s important to understand is that a finalizer increases the life of an object. Because the finalization code also has to run, the .NET Framework keeps a reference to the object in a special finalization queue. An additional thread runs all the finalizers at a time deemed appropriate based on the execution context. This delays garbage collection for types that have a finalizer.

This is not an ideal situation. You shouldn’t depend on the garbage collector to run a finalizer at some point in time to close your file. Instead, you should do this yourself. To offer you the opportunity of explicitly freeing unmanaged resources, C# offers the idea of the IDisposable interface that you can see in Example 2-82.

Example 2-82. The IDisposable interface

public interface IDisposable

{

void Dispose();

}

The IDiposable interface offers one method: Dispose, which will free any unmanaged resources immediately. The example from Example 2-82 could also have been written as in Example 2-83.

Example 2-83. Calling Dispose to free unmanaged resources

StreamWriter stream = File.CreateText("temp.dat");

stream.Write("some data");

stream.Dispose();

File.Delete("temp.dat");

But what if an exception would occur before stream.Dispose() is called? To make sure that your resources are always cleaned up, you need to wrap all types that implement IDisposable in a try/finally statement. Because this is so common, C# has a special statement for this: the using statement. The using statement is translated by the compiler in a try/finally statement that calls Dispose on the object. Because of this, the using statement can be used only with types that implement IDisposable.

using (StreamWriter sw = File.CreateText("temp.dat"))

{ }

The using statement ensures that Dispose is always called. Every type that implements IDisposable should be used in a using statement whenever possible. This way you make sure that you clean up all unmanaged resources.

After disposing an item, you can’t use it any more. Using a disposed item will result in an ObjectDisposedException.

If you also want to use a catch statement when working with an IDisposable object, you need to do this manually by writing a try/catch/finally statement where you call Dispose in the finally clause.

MORE INFO: EXCEPTION HANDLING

For more information on how to use exception handling, see Objective 1.5: Implement exception handling.

Implementing IDisposable and a finalizer

Creating your own custom type that implements IDisposable and a finalizer correctly is not a trivial task.

For example, suppose you have a wrapper class around an unmanaged file resource. You implement IDisposable so users of your class can immediately clean up if they want. You also implement a finalizer in case they forget to call Dispose. Example 2-84 shows how to do this.

Example 2-84. Implementing IDisposable and a finalizer

using System;

using System.IO;

class UnmangedWrapper : IDisposable

{

public FileStream Stream { get; private set; }

public UnmangedWrapper()

{

this.Stream = File.Open("temp.dat", FileMode.Create);

}

~UnmangedWrapper()

{

Dispose(false);

}

public void Close()

{

Dispose();

}

public void Dispose()

{

Dispose(true);

System.GC.SuppressFinalize(this);

}

public void Dispose(bool disposing)

{

if (disposing)

{

if (Stream != null)

{

Stream.Close();

}

}

}

}

There are a couple of things to notice about this implementation:

§ The finalizer only calls Dispose passing false for disposing.

§ The extra Dispose method with the Boolean argument does the real work. This method checks if it’s being called in an explicit Dispose or if it’s being called from the finalizer:

§ If the finalizer calls Dispose, you do nothing because the Stream object also implements a finalizer, and the garbage collector takes care of calling the finalizer of the Stream instance. You can’t be sure if it’s already called, so it’s best to leave it up to the garbage collector.

§ If Dispose is called explicitly, you close the underlying FIleStream. It’s important to be defensive in coding this method; always check for any source of possible exceptions. It could be that Dispose is called multiple times and that shouldn’t cause any errors.

§ The regular Dispose method calls GC.SuppressFinalize(this) to make sure that the object is removed from the finalization list that the garbage collector is keeping track of. The instance has already cleaned up after itself, so it’s not necessary that the garbage collector call the finalizer.

EXAM TIP

It’s important to know the difference between implementing IDisposable and a finalizer. Both clean up your object, but a finalizer is called by the garbage collector, and the Dispose method can be called from code.

Weak references

Sometimes you have to work with large objects that require a lot of time to create. For example, a list of objects that has to be retrieved from a database. It would be nice if you can just keep the items in memory; however, that increases the memory load of your application, and maybe the list won’t be needed any more. But if garbage collection hasn’t occurred yet, it would be nice if you could just reuse the list you created.

This is where the type WeakReference can be used. A WeakReference, as the name suggests, doesn’t hold a real reference to an item on the heap, so that it can’t be garbage collected. But when garbage collection hasn’t occurred yet, you can still access the item through the WeakReference. Example 2-85 shows how to use a WeakReference.

Example 2-85. Using WeakReference

static WeakReference data;

public static void Run()

{

object result = GetData();

// GC.Collect(); Uncommenting this line will make data.Target null

result = GetData();

}

private static object GetData()

{

if (data == null)

{

data = new WeakReference(LoadLargeList());

}

if (data.Target == null)

{

data.Target = LoadLargeList();

}

return data.Target;

}

The GetData function checks that the WeakReference still contains data. If not, the data is loaded again and saved in the WeakReference. The interesting thing is that uncommenting the line GC.Collect() frees the memory that the WeakReference points to. If garbage collection has not occurred, the data inside WeakReference.Target can be accessed and returned to the caller.

Using WeakReference is not a complete solution for a caching scenario. If you want to implement a cache, you should define an algorithm that decides which items should be removed from the cache. Upon removing, you turn a reference into a WeakReference and leave it up to the garbage collector.

THOUGHT EXPERIMENT

Cleaning up your stuff

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You have created your first Windows 8 app. It’s a nice game that enables users to take a video of themselves describing a word. Others have to guess; that way, they can earn points that enable them to create a longer video.

One day you wake up and you suddenly realize that Microsoft chose your app as its app of the week. Your web server that’s running all the logic of the game is trembling under the user load because of the sudden popularity. Both memory and CPU pressure are a lot higher than you expected. You have some types that are qualified to be a value type but at the time of creating your app, you just used classes.

1. How can using value types when possible improve your performance? Or could it be that your performance will deteriorate more?

2. Why is implementing IDisposable important to reduce memory pressure? Is it always the best to call Dispose on an element as soon as you are done with it?

3. Should you implement a finalizer on all your types that implement IDisposable?

4. You have some items that are used a lot. Would it be wise to put them in a static field so you don’t have to re-create them each time?

Objective summary

§ Memory in C# consists of both the stack and the heap.

§ The heap is managed by the garbage collector.

§ The garbage collector frees any memory that is not referenced any more.

§ A finalizer is a special piece of code that’s run by the garbage collector when it removes an object.

§ IDisposable can be implemented to free any unmanaged resources in a deterministic way.

§ Objects implementing IDisposable can be used with a using statement to make sure they are always freed.

§ A WeakReference can be used to maintain a reference to items that can be garbage collected when necessary.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You are about to execute a piece of code that is performance-sensitive. You are afraid that a garbage collection will occur during the execution of this code. Which method should you call before executing your code?

a. GC.RemoveMemoryPressure()

b. GC. SuppressFinalize()

c. GC.Collect()

d. GC.WaitForPendingFinalizers()

2. An object that is implementing IDisposable is passed to your class as an argument. Should you wrap the element in a using statement?

a. Yes, otherwise a memory leak could happen.

b. No, you should call Close on the object.

c. No, you should use a try/finally statement and call Dispose yourself.

d. No, the calling method should use a using statement.

3. Your application is using a lot of memory. Which solution should you use?

a. Turn all references into WeakReferences.

b. Set all references to null when you are done with them.

c. Use a caching algorithm to decide which objects can be freed.

d. Use a background thread to call GC.Collect() on a scheduled interval.

Objective 2.7: Manipulate strings

Text is one of the most important concepts in almost every application, so C# offers quite a lot of support when working with text. All this support revolves around the built-in type System.String. When creating your applications, you often manipulate strings. For example, adding strings together, searching for a certain string or character within another string, iterating over a string, and then finally formatting them for display is a common practice. You will learn how to perform all those steps while keeping an eye on both usability and performance.

THIS OBJECTIVE COVERS HOW TO:

§ Manipulate strings.

§ Search strings.

§ Enumerate strings.

§ Format strings.

Using strings in the .NET Framework

A string in C# is an object of type String whose value is text. The string object contains an array of Char objects internally. A string has a Length property that shows the number of Char objects it contains. String is a reference type that looks like value type (for example, the equality operators == and != are overloaded to compare on value, not on reference).

In C#, you can refer to a string both as String and string. You can use whichever naming convention suits you. The string keyword is just an alias for the .NET Framework’s String.

One of the special characteristics of a string is that it is immutable, so it cannot be changed after it has been created. Every change to a string will create a new string. This is why all of the String manipulation methods return a string.

Immutability is useful in many scenarios. Reasoning about a structure if you know it will never change is easier. It cannot be modified so it is inherently thread-safe. It is more secure because no one can mess with it. Suddenly something like creating undo-redo is much easier, your data structure is immutable and you maintain only snapshots of your state.

But immutable data structures also have a negative side. The code in Example 2-86 looks innocent, but it will create a new string for each iteration in your loop. It uses a lot of unnecessary memory and shows why you have to use caution when working with strings.

Example 2-86. Creating a large number of strings

string s = string.Empty;

for (int i = 0; i < 10000; i++)

{

s += "x";

}

This code will run 10,000 times, and each time it will create a new string. The reference s will point only to the last item, so all other strings are immediately ready for garbage collection.

Because C# is aware of this problem, the compiler tries to optimize working with strings for you. When creating two identical string literals in one compilation unit, the compiler ensures that only one string object is created by the CLR. This is called string interning, which is done only at compile time. Doing it at runtime would incur too much of a performance penalty (searching through all strings every time you create a new one is too costly).

When working with such a large number of string operations, you have to keep in mind that string is immutable and that the .NET Framework offers some special helper classes when dealing with strings.

EXAM TIP

Because of the immutability of the string type, all string operations return a new string. Make sure that you use this value instead of the original string.

Manipulating strings

Because of the immutability of strings, the .NET Framework offers several classes that can be used when building strings.

StringBuilder

The StringBuilder class can be used when you are working with strings in a tight loop. Instead of creating a new string over and over again, you can use the StringBuilder, which uses a string buffer internally to improve performance. The StringBuilder class even enables you to change the value of individual characters inside a string (see Example 2-87).

Example 2-87. Changing a character with a StringBuilder

System.Text.StringBuilder sb = new System.Text.StringBuilder("A initial value");

sb[0] = 'B';

Our previous example of concatenating a string 10,000 times can be rewritten with a StringBuilder, as Example 2-88 shows.

Example 2-88. Using a StringBuilder in a loop

StringBuilder sb = new StringBuilder(string.Empty);

for (int i = 0; i < 10000; i++)

{

sb.Append("x");

}

One thing to keep in mind is that the StringBuilder does not always give better performance. When concatenating a fixed series of strings, the compiler can optimize this and combine individual concatenation operations into a single operation. When you are working with an arbitrary number of strings, such as in the loop example, a StringBuilder is a better choice (in this example, you could have also used new String(“x”, 10000) to create the string; when dealing with more varied data, this won’t be possible).

StringWriter and StringReader

Some APIs in the .NET Framework expect a TextWriter or TextReader to work. Those APIs can’t work with a string or StringBuilder directly. Because of this, the .NET Framework adds a StringReader and StringWriter class. These classes adapt the interface of the StringBuilder so they can be used in places where a TextWriter or TextReader is expected.

Internally, StringWriter and StringReader use a StringBuilder. The only thing they do is adapt the interface of the StringBuilder to that of the TextWriter and TextReader.

One of the methods in the .NET Framework that expects an instance of TextWriter is Xml-Writer.Create. Normally, you pass an instance of StreamWriter so that you can create a new XML file. But when you want the resulting XML only in memory, you can pass a StringWriter (see Example 2-89).

Example 2-89. Using a StringWriter as the output for an XmlWriter

var stringWriter = new StringWriter();

using (XmlWriter writer = XmlWriter.Create(stringWriter))

{

writer.WriteStartElement("book");

writer.WriteElementString("price", "19.95");

writer.WriteEndElement();

writer.Flush();

}

string xml = stringWriter.ToString();

The value of xml is now:

<?xml version=\"1.0\" encoding=\"utf-16\"?>

<book>

<price>19.95</price>

</book>

When using the XmlReader, you can parse some XML and access the individual elements. XmlReader expects an instance of TextWriter, so you can pass it a StringReader (see Example 2-90).

Example 2-90. Using a StringReader as the input for an XmlReader

var stringReader = new StringReader(xml);

using (XmlReader reader = XmlReader.Create(stringReader))

{

reader.ReadToFollowing("price");

decimal price = decimal.Parse(reader.ReadInnerXml(),

new CultureInfo("en-US")); // Make sure that you read the decimal part correctly

}

Searching for strings

When working with strings, you often look for a substring inside another string (to parse some content or to check for valid user input or some other scenario).

The String class offers a couple of methods that can help you perform all kinds of search actions. The most common are IndexOf, LastIndexOf, StartsWith, EndsWith, and SubString.

One thing to keep in mind is that string methods can be culture sensitive. This is why most of the methods accept an instance of the StringComparison enumeration. When working with strings, always try to avoid the methods that don’t use an explicit value of StringComparison.

MORE INFO: BEST PRACTICES WHEN WORKING WITH STRINGS

For more information on best practices when working with strings, see the MSDN documentation at http://msdn.microsoft.com/en-us/library/dd465121.aspx.

IndexOf returns the index of the first occurrence of a character or substring within a string. If the value cannot be found, it returns -1. The same is true with LastIndexOf, except this method begins searching at the end of a string and moves to the beginning. Look at Example 2-91 for an example of IndexOf and LastIndexOf.

Example 2-91. Using IndexOf and LastIndexOf

string value = "My Sample Value";

int indexOfp = value.IndexOf('p'); // returns 6

int lastIndexOfm = value.LastIndexOf('m'); // returns 5

StartsWith and EndsWith see whether a string starts or ends with a certain value, respectively. It returns true or false depending on the result. Example 2-92 shows how to use them.

Example 2-92. Using StartsWith and EndsWith

string value = "<mycustominput>";

if (value.StartsWith("<")) { }

if (value.EndsWith(">")) { }

Substring can be used to retrieve a partial string from another string. You can pass a start and a length to Substring. If necessary, you can calculate these indexes by using IndexOf or LastIndexOf. Example 2-93 shows how to use Substring.

Example 2-93. Reading a substring

string value = "My Sample Value";

string subString = value.Substring(3, 6); // Returns 'Sample'

Another way to search a string is by using a regular expression, which uses a patternmatching notation that can quickly parse large amounts of text looking for a specific format. Regular expressions can be useful when validating user input (such as an e-mail address, ZIP code, or date). The code in Example 2-94 strips all titles from the names that you pass it. Imagine how much work it would have been to create this when using IndexOf and SubString.

Example 2-94. Changing a string with a regular expression

string pattern = "(Mr\\.? |Mrs\\.? |Miss |Ms\\.? )";

string[] names = { "Mr. Henry Hunt", "Ms. Sara Samuels",

"Abraham Adams", "Ms. Nicole Norris" };

foreach (string name in names)

Console.WriteLine(Regex.Replace(name, pattern, String.Empty));

Enumerating strings

A string is an array of characters. You can enumerate a string just as if it were a typical collection. Because a string implements IEnumerable and IEnumerable<Char>, it exposes the GetEnumerator method that you can use to iterate over a string.

You can use a string in a foreach loop to check all individual characters, as Example 2-95 shows.

Example 2-95. Iterating over a string

string value = "My Custom Value";

foreach (char c in value)

Console.WriteLine(c);

Splitting a string in words and then iterating over them is also possible. The following line splits the sentence on spaces; it returns an IEnumerable that can then be iterated:

foreach (string word in "My sentence separated by spaces".Split(' ')) { }

Of course, this is a simplified version. When dealing with large amounts of text that contain all kinds of punctuation you need to use a regular expression to split the words.

Formatting strings

When displaying strings to the user, you want to make sure that they are in the right format. Especially when working with culture-sensitive data such as DateTime or numbers, it’s important to make sure that the string is displayed in a manner that is suitable for current users and their settings.

Formatting is the process of converting an instance of a type to a string representation. When converting an instance to a string, the basic way of doing this is to call the ToString method that’s defined as a virtual member on System.Object. Overriding ToString is a good practice. If you don’t do this, ToString will return by default the name of your type. When you override ToString, you can give it a more meaningful value, as Example 2-96 shows.

Example 2-96. Overriding ToString

class Person

{

public Person(string firstName, string lastName)

{

this.FirstName = firstName;

this.LastName = lastName;

}

public string FirstName { get; set; }

public string LastName { get; set; }

public override string ToString()

{

return FirstName + LastName;

}

}

Person p = new Person("John", "Doe");

Console.WriteLine(p); // Displays 'John Doe'

When an object has multiple string representations, overriding ToString is not enough. For example, a Temperature object can display its temperature in degrees Fahrenheit, Celsius, or Kelvin. An integer value can also be displayed in multiple ways. Maybe it represents a phone number or an amount of money.

To enable this kind of behavior, you can use format strings, which are strings that describe how an object should display. The .NET Framework uses them for numeric types, dates, times, and enumerations, as Example 2-97 shows.

Example 2-97. Displaying a number with a currency format string

double cost = 1234.56;

Console.WriteLine(cost.ToString("C",

new System.Globalization.CultureInfo("en-US")));

// Displays $1,234.56

You can use the same approach when displaying a date and time value. Depending on the culture, the formatted output can be completely different. Example 2-98 shows how to use different format strings with a DateTime for an English culture.

Example 2-98. Displaying a DateTime with different format strings

DateTime d = new DateTime(2013, 4, 22);

CultureInfo provider = new CultureInfo("en-US");

Console.WriteLine(d.ToString("d", provider)); // Displays 4/22/2013

Console.WriteLine(d.ToString("D", provider)); // Displays Monday, April 22, 2013

Console.WriteLine(d.ToString("M", provider)); // Displays April 22

Providing the correct CultureInfo is important when formatting values. It contains all the necessary information about how a particular type is displayed in that culture. In the same way, it’s important to make sure that when you save values to a database; for example, you do this in a culture-insensitive way. If the culture-insensitive data is than loaded, it can be formatted depending on the user who is viewing the data.

You can also implement this custom formatting on your own types. You do this by creating a ToString(string) method on your type. When doing this, make sure that you are compliant with the standard format strings in the .NET Framework.

For example, a format string of G should represent a common format for your object (the same as calling ToString()) and a null value for the format string should also display the common format. Example 2-99 shows how to do this.

Example 2-99. Implementing custom formatting on a type

class Person

{

...

public string ToString(string format)

{

if (string.IsNullOrWhiteSpace(format) || format = "G") format = "FL";

format = format.Trim().ToUpperInvariant();

switch (format)

{

case "FL":

return FirstName + " " + LastName;

case "LF":

return LastName + " " + FirstName;

case "FSL":

return FirstName + ", " + LastName;

case "LSF":

return LastName + ", " + FirstName;

default:

throw new FormatException(String.Format(

"The '{0}' format string is not supported.", format));

}

}

}

IFormatProvider and IFormattable

When formatting strings, you can also use an IFormatProvider. The IFormatProvider has one method, GetFormat(Type), which returns specific formatting information for formatting a type. All CultureInfo objects implement IFormatProvider. The CultureInfo object returns a culture-specificNumberFormatInfo or DateTimeFormatInfo if a string or DateTime is formatted. That way, you can format a string as culture specific by passing a CultureInfo object to the ToString method.

When implementing your own ToString formatting method on a type, you can also choose to accept an IFormatProvider. When doing this, you can implement the IFormattable interface. Using IFormattable makes sure that you can integrate with the .NET Framework when it comes to formatting strings.

When implementing IFormattable, you have support for string conversion by the Convert class (which has an overload that accepts an object and IFormatProvider). You can also support composite formatting, in which your type is used to create a composite string with other types (see Example 2-100).

Example 2-100. Creating a composite string formatting

int a = 1;

int b = 2;

string result = string.Format("a: {0}, b: {1}", a, b);

Console.WriteLine(result); // Displays 'a: 1, b: 2'

Formatting strings is a large area in the .NET Framework. It pays to be familiar with the possible options and to know where you can find more information when you need it. Make sure that you use the correct CultureInfo when formatting a string and use format strings to ensure that you get the correct result.

THOUGHT EXPERIMENT

Showing some text

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are working on a localized application to be used for time tracking. A team can use it to track time for various projects and tasks, and make sure that all billable time is correct. The application consists of a web front end based on ASP.NET and a desktop application that uses C#. Suddenly, your manager announces that your application is going global. You currently support only the English language; you didn’t take globalization into account when first architecting the application. Now you have to support Spanish and German.

1. Make a list of the things you have to keep in mind when updating your application for globalization.

Objective summary

§ A string is an immutable reference type.

§ When doing a lot of string manipulations, you should use a StringBuilder.

§ The String class offers a lot of methods for dealing with strings like IndexOf, LastIndexOf, StartsWith, EndsWith, and Substring.

§ Strings can be enumerated as a collection of characters.

§ Formatting is the process of displaying an object as a string.

§ You can use format strings to change how an object is converted to a string.

§ You can implement formatting for your own types.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to display only the date portion of a DateTime according to the French culture. What method should you use?

a. dt.ToString(new CultureInfo(“fr-FR”))

b. dt.ToString(“M”, new CultureInfo(“fr-FR”));

c. dt.ToString(“d”);

d. dt.ToString(“d”, new CultureInfo(“fr-FR”));

2. You want your type to be able to be converted from string. Which interface should you implement?

a. IFormattable

b. IFormatProvider

c. IComparable

d. IConvertible

3. You are parsing a large piece of text to replace values based on some complex algorithm. Which class should you use?

a. StringReader

b. StringBuilder

c. StringWriter

d. String

Chapter summary

§ C# uses types such as class, struct, interface, and enum. Types have can have members such as methods, events, fields, properties, indexed properties, and constructors.

§ When working with types, you sometimes need to convert between them. This can be done either implicitly or explicitly. When creating your own types, you can add support for these types of conversions.

§ You use accessors such as public, private, protected, internal, and protected internal to enforce encapsulation and accessibility. Properties can be used to encapsulate data.

§ An object hierarchy can be created by inheritance, and you can have both interface and class inheritance. By marking members as virtual, a derived class can override the member.

§ Reflection is the process of inspecting metadata at runtime. Attributes can be used to add metadata to a type.

§ C# is a managed language, which means that a garbage collector makes sure that all managed objects are freed from memory whenever they are no longer in use.

§ Strings can be used for text. The string type is immutable. The .NET Framework offers a StringBuilder for manipulating large amounts of text. Strings can be searched, enumerated, and formatted for display.

Answers

This section contains the solutions to the thought experiments and answers to the lesson review questions in this chapter.

Objective 2.1: Thought experiment

1. Some of the types that you can use in building your web shop are: Order, OrderLine, Product, Customer, Review, SearchCriteria, BusinessRule.

2. When designing the system, you should focus on the behavior and then make sure that you have the data to support it. For example, instead of publicly exposing the OrderLines that an order contains, you should expose a method AddProduct(product, amount) that creates an OrderLineinternally and makes sure that the Order follows all business rules.

3. By making sure that you have the correct constructors, users of your types can easily see which data is required. By using enums (for example for the Order status and Customer status), you can improve readability of your code. By using a base class for your business rules you can make the system more extensible so that other business rules can be easily defined.

Objective 2.1: Review

1. Correct answer: B

a. Incorrect: A constructor is used to create an instance of a new type.

b. Correct: An indexer property enables the user of the type to easily access a type that represents an array-like collection.

c. Incorrect: Making the type generic enables you to store multiple different types inside your collection.

d. Incorrect: A static property cannot access the instance data of the collection.

2. Correct answer: A

a. Correct: Constraining your generic type parameter to class allows the class to be used only with a reference type.

b. Incorrect: This will constrain the class to be used with a value type, not a reference type.

c. Incorrect: This will constrain the class to be used with a type that has an empty default constructor. It can be both a value and a reference type.

d. Incorrect: This constrains the class to be used with a type that implements the IDisposable interface.

3. Correct answer: C

a. Incorrect: Passing a noninitialized struct will result in a compile error of using an unassigned local variable.

b. Incorrect: A struct can be changed inside a method. It won’t change the original struct that was passed in, however.

c. Correct: Passing a struct will make a copy of the data. The copy can be changed; the original won’t change with it.

d. Incorrect: With a reference type, the method can make changes that will reflect on the original. Because a value type is copied, it won’t change the original. Returning the changes from the method will again create a new instance that will overwrite the original.

Objective 2.2: Thought experiment

1. ArrayLists are nongeneric; they can only work with items of type object. Because of this, you have to box and unbox your calculation parameters each time you use them. Switching to a generic collection will avoid all the boxing and unboxing and will improve performance.

2. Throwing and catching exceptions is expensive. You can avoid the exceptions when converting items by first making sure that the conversion is allowed by using the is and as keywords. You can use a simple Boolean check to see whether a conversion is allowed.

3. One thing you can do is make sure to implement implicit conversions to the CLR types that you want to support. You could also add a helper class that aids in converting items.

Objective 2.2: Review

1. Correct answer: B

a. Incorrect: A conversion between a custom class and a value type does not exist by default.

b. Correct: Adding an implicit operator will enable users of your class to convert between Distance and double without any extra work.

c. Incorrect: Although adding an explicit cast operator will enable users of the class to convert from Distance to double, they will still need to explicitly cast it.

d. Incorrect: A Parse method is used when converting a string to a type. It doesn’t add conversions from your type to another type.

2. Correct answers: A, C

a. Correct: The as operator will return null if the conversion failed. If it succeeds, it will return the converted object. Seeing whether the result is null enables you to check for a valid conversion.

b. Incorrect: Implicitly casting something of type object to another type is not possible. It would require an explicit cast.

c. Correct: The is keyword will see whether a type is derived from another type.

d. Incorrect: The dynamic keyword can be used when you want weakly typing. It will still throw errors at runtime if an action is not possible.

3. Correct answer: A

a. Correct: Using a generic collection will eliminate the need to box and unbox values. This will improve performance, especially when working with a large number of items.

b. Incorrect: Changing Point to be a reference type could increase memory usage. You will still have to convert from object to Point when using the nongeneric ArrayList.

c. Incorrect: Point is a struct that inherits from System.ValueType, which in turn inherits from System.Object. The implicit conversion is already present; adding it won’t improve performance.

d. Incorrect: Making the collection dynamic will loosen compile-time checking. It won’t improve performance because the runtime has to do extra work.

Objective 2.3: Thought experiment

1. Making each and every type public will remove all the benefits that encapsulation offers. Encapsulating data means that the internal representation of an object is hidden from the outside world. This prevents users of your type from setting an invalid or inconsistent state, and also hides complexity. This makes your system more robust because you can change implementation details while not changing the public interface of your type.

2. Because a property offers a controlled way of accessing a field, you can run extra code both on reading and changing the value. You can, for example, make sure that only authorized personnel can change specific values that determine who can use a specific chemical.

3. Because a type can implement multiple interfaces, you can use an interface to expose only a subset of the type members. By using explicit interface implementation, you can also hide certain members that should be visible under only certain circumstances. This way, you can enhance the public interface of a type by exposing fewer members at a given time.

Objective 2.3: Review

1. Correct answer: D

a. Incorrect: A public class with public members can be accessed from other assemblies without any restrictions.

b. Incorrect: Types in other assemblies can derive from the class and access the protected methods.

c. Incorrect: Types in other assemblies cannot derive from the class, but other types in the same assembly can access the method.

d. Correct: An internal class cannot be accessed outside of its assembly. The protected methods can be accessed only by derived types.

2. Correct answer: B

a. Incorrect: A protected field cannot be read by other types outside of the class.

b. Correct: A public property can be read by all other types. The protected set modifier restricts changes to derived types.

c. Incorrect: A protected property cannot be read by other types outside of the class.

d. Incorrect: This will generate a compile error because the accessibility modifier of the get accessor must be more restrictive than the property. Public is less restrictive than protected.

3. Correct answer: A

a. Correct: Implementing IA implicitly will make this the default implementation. When dealing with a reference to the class, this method will be invoked. Implementing IB explicitly will invoke the implementation for IB when dealing with a reference to the IB interface.

b. Incorrect: When both IA and IB are implemented explicitly, you need to cast a reference to the class to one or both interface types to invoke the method.

c. Incorrect: Implementing both IA and IB implicitly won’t allow for a different implementation for IB.

d. Incorrect: Implementing IB implicitly makes IB the default implementation instead of IA.

Objective 2.4: Thought experiment

1. The problem is that C# doesn’t have multiple inheritance for classes, but you can have multiple interface inheritance. When types such as IDisposable would be implemented as a base class, you would be able to inherit only from that class. Inheriting from other custom types would be impossible.

2. Implementing the search criteria can be best done through using class inheritance. By making an abstract class that accepts an input and then executes a specific filter over the items, you can reuse as much code as possible. The inherited criteria would need to specify only the filter expression (maybe through a lambda) and their exact parameters through the constructor.

3. IUnknown is not usable in this scenario; it’s only for dealing with unmanaged code. Implementing IDisposable should be used when you are dealing with unmanaged resources that you want to free. In this case, that’s not necessary. IComparable could come in handy when you want to order your criteria (for example, one criterion should execute before any of the others). IEnumerable can be used to implement the iterator pattern on your criteria. This could give you a performance benefit. Instead of processing all the houses at once, you can return an iterator and filter the whole set one by one.

Objective 2.4: Review

1. Correct answer: C

a. Incorrect: An interface won’t let you share any implementation code, only the public member signatures.

b. Incorrect: A class requires you to have an implementation for every member. It doesn’t give you the option to only declare a member signature.

c. Correct: An abstract class enables you to share both implemented methods and method signatures that a derived class needs to implement.

d. Incorrect: A sealed class can’t be inherited.

2. Correct answer: B

a. Incorrect: IEnumerable should be implemented on collection-like types so they can be easily iterated over.

b. Correct: IComparable enables objects to be compared to each other. It returns an integer value that represents the relative position as smaller than 0, 0 or larger than 0.

c. Incorrect: IDisposable should be implemented on types that access unmanaged resources and that need to release them.

d. Incorrect: IUnknown is used only when working with the COM.

3. Correct answers: B, D

a. Incorrect: When you mark the base type as abstract, you can’t create an instance of it. You can’t use both the base and the derived type as concrete types in your code.

b. Correct: Marking the method in the base class as virtual enables it to be overridden by derived classes.

c. Incorrect: The new keyword hides the method in the base class. You shouldn’t use it when you want to extend the behavior from the base class.

d. Correct: The override keyword enables you to override a method marked as virtual in a base class.

Objective 2.5: Thought experiment

1. Although interface inheritance describes an “is-a-kind-of” relation, but you can’t say that your type is “a kind of AuthorizedObject.” Instead, you use attributes to say something about another type, which is why C# contains metadata. Inheritance should not be used to decorate a type with certain information.

2. Reflection can be a really slow process. You can replace reflection with expression trees. When you are mapping data to a custom type, you can build an expression tree (and cache it!). The expression tree can be compiled to native code. Of course, you will still have the penalty of doing this extra step, but it will be much faster than reflection.

3. For generating code, you can use the CodeDOM. A CompilationUnit created by the CodeDOM can be outputted as a source file.

Objective 2.5: Review

1. Correct answers: A, D

a. Correct: The field is a nonstatic instance field.

b. Incorrect: DeclaredOnly is used when you don’t want to include inherited members.

c. Incorrect: The field is not static; it’s a per-instance field.

d. Correct: Nonpublic is necessary because the field is private.

2. Correct answer: B

a. Incorrect: The AttributeTargets.GenericParameter can be applied to generic parameters. It’s not used for regular method arguments.

b. Correct: The Attribute targets both Methods and Parameters. It can also be applied multiple times.

c. Incorrect: With AttributeTargets.All, the attribute can be applied to all types. It also can’t be applied multiple times.

d. Incorrect: Because AllowMultiple is false by default, this attribute can’t be applied multiple times.

3. Correct answer: C

a. Incorrect: An Action doesn’t return a value. It also takes a Boolean input instead of the list of strings and the filter value.

b. Incorrect: This delegate doesn’t have a parameter for the value to filter on.

c. Correct: It takes both the input list and the value to filter on and returns the filtered list.

d. Incorrect: This returns only a list of strings. It doesn’t have an argument for the filter parameter or the original list.

Objective 2.6: Thought experiment

1. Reference types are on the heap and managed by the garbage collector. Value types are on the stack most of the time and are freed when the current method ends. When a value type is on the stack, it takes up less memory than it would on the heap. However, if a value type is enclosed in a reference type, it’s still on the heap. Changing the type to a struct is no guarantee that your memory pressure will drop.

2. IDisposable gives you a way to explicitly free up unmanaged resources and you won’t have to wait for the garbage collector to free memory. However, calling Dispose is something that takes up CPU time. When leaving things to the garbage collector, the garbage collector will decide when is the best time to start calling finalizers. There is a compromise to make between explicitly or implicitly freeing your resources.

3. A class that implements IDisposable should have some unmanaged resources. A finalizer is important for making sure that an item always frees the unmanaged resources, even when Dispose is not called. However, a class with a finalizer is threaded in a special way. The object is added to a finalization queue and is kept in memory until the finalizer has run. It’s important to make sure that when Dispose is called, the object is explicitly removed from the finalization queue.

4. A static field is a root reference; the garbage collector will never free an object that is referenced by a static field. In this case, it would be better to use a cache that keeps track of which objects should be kept in memory. A caching algorithm that decides on how frequently an object is accessed can make sure that your objects are around. However, when there is a memory shortage, the cache can turn some objects into WeakReferences and make sure they can be garbage collected.

Objective 2.6: Review

1. Correct answer: C

a. Incorrect: RemoveMemoryPressure should be called only after calling AddMemoryPressure to inform the runtime that a managed object uses a large amount of unmanaged memory.

b. Incorrect: SuppressFinalize should be called in the Dispose method of an object to inform the runtime that the object doesn’t need to be finalized any more.

c. Correct: Collect will execute a garbage collection, freeing as much memory as possible at that time, which can be a very expensive process. This won’t prevent the garbage collector from executing during your time-sensitive code, but it will make it less likely to happen.

d. Incorrect: WaitForPendingFinalizers suspends the current thread so all finalizers that are on the finalization queue can run. This will free some memory (for all objects that are waiting for finalization). Normally, however, you call this code after calling Collect if you want to make sure that all finalizers have run.

2. Correct answer: D

a. Incorrect: A memory leak won’t happen because the finalizer of the class will eventually execute and dispose of the element. Disposing of the object in your method will cause an ObjectDisposedException to be thrown in other code that uses the object.

b. Incorrect: The Close method is sometimes used as a secondary method that internally calls Dispose. It’s implemented on types such as File. The same reasoning applies as with answer A.

c. Incorrect: A using statement is equivalent to a try/finally statement with a Dispose call. However, you don’t want to dispose of the item because the calling code could depend on the object being in a usable state.

d. Correct: The calling code knows what the lifetime of the object should be and should decide when to dispose of the object.

3. Correct answer: C

a. Incorrect: A WeakReference is not an equivalent to an efficient caching strategy. Turning all items into WeakReferences will complicate your code (you have to see whether the memory is cleared). It could potentially increase your memory usage because the WeakReferenceitself also takes up memory.

b. Incorrect: Setting references to null is optimized away by the compiler. Unlike some other languages, it doesn’t explicitly free any memory.

c. Correct: A caching strategy is the best solution. You can decide whether you want to free memory based on usage, a timestamp, or some other criteria.

d. Incorrect: Calling GC.Collect on a scheduled interval won’t improve your memory usage. Memory is freed only when there are no root references to an object. GC.Collect will stall your execution thread, making things slower, while not freeing any more memory than waiting for a regular Collect to take place.

Objective 2.7: Thought experiment

This is not a trivial task to do. Building globalization into an existing application requires more effort than taking it into account from the start.

The areas you will have to focus on are the display of text. Especially because this is a time tracking application there will be a lot of dates and times that are displayed.

There are a few areas you will have to focus on:

§ Make sure that all string comparisons use an explicit overload that takes a StringComparison object.

§ Use StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when comparing strings in a culture-agnostic way.

§ Use StringComparison.CurrentCulture when displaying text to the user.

§ Make sure that all persisted text is persisted with the invariant culture.

§ Make sure that you are not using String.Compare or CompareTo for equality testing.

§ Implement IFormattable for custom types that are displayed to the user to make sure that all culture settings are respected.

§ Use the correct format strings when displaying numbers, dates, and times to the user.

Objective 2.7: Review

1. Correct answer: D

a. Incorrect: Only specifying the culture will give you the full date and time.

b. Incorrect: Specifying “M” as the format string results in “22 avril” without the year.

c. Incorrect: This will give the date in the correct format, but not with the French culture.

d. Correct: This will give the date in the correct French format.

2. Correct answer: A

a. Correct: IFormattable provides the functionality to format the value of an object into a string representation. It is also used by the Convert class to do the opposite.

b. Incorrect: IFormatProvider is used to retrieve an object that controls formatting, not the actual formatting.

c. Incorrect: IComparable is used to sort items.

d. Incorrect: IConvertible defines methods to convert a type to an equivalent CLR type.

3. Correct answer: B

a. Incorrect: StringReader is an adapter of the StringBuilder class so that it can be used in places where a TextReader is required.

b. Correct: The StringBuilder class is most efficient when changing large amounts of strings.

c. Incorrect: The StringWriter is used in places where a TextWriter is required. It’s an adapter of the StringBuilder.

d. Incorrect: The regular String class is immutable, so it’s not efficient to use when changing large amounts of strings.