第3课: 构造类

In object-oriented languages, the bulk of the work should be performed within objects. All but the simplest applications require constructing one or more custom classes, each with multiple properties and methods used to perform tasks related to that object. This lesson discusses how to create custom classes.

 

什么是继承?

The .NET Framework has thousands of classes, and each class has many different methods and properties. Keeping track of all these classes and members would be impossible if the .NET Framework were not implemented extremely consistently. For example, every class has a ToString method that performs exactly the same task—converting an instance of the class into a string. Similarly, many classes support the same operators, such as comparing two instances of a class for equality.

因为继承( inheritance )和 接口(interfaces)  这种一致性是可能的。(described in the next section). 通过继承,可以从一个存在的类来创建一个新类. For example, you will learn in Chapter 6, "Graphics," that the Bitmap class inherits from the Image class and extends it by adding functionality. Therefore, you can use an instance of the Bitmap class in the same ways that you would use an instance of the Image class. However, the Bitmap class provides additional methods that enable you to do more with pictures.

通过继承 System.ApplicationException ,你可以很容易地创建自定义异常类  ,如下所示:

' VB
Class DerivedException
Inherits System.ApplicationException

Public Overrides ReadOnly Property Message() As String
Get
Return "An error occurred in the application."
End Get
End Property
End Class

// C#
class DerivedException : System.ApplicationException
{

public override string Message
{
get { return "An error occurred in the application."; }
}
}

你可以抛出和捕捉新的异常,因为自定义类继承了它的基类的所有行为,如下所示:

' VB
Try
Throw New DerivedException
Catch ex As DerivedException
Console.WriteLine("Source: {0}, Error: {1}", ex.Source, ex.Message)
End Try

// C#
try
{
throw new DerivedException();
}
catch (DerivedException ex)
{
Console.WriteLine("Source: {0}, Error: {1}", ex.Source, ex.Message);
}

注意:自定义类不仅仅支持throw/catch 行为, 而已也包含继承自System.ApplicationException 的原始成员(其它类同理).

继承的另外ygie好处是:可以变换地使用继承类. For example, there are five classes that inherit from the System.Drawing.Brush base class: HatchBrush, LinearGradientBrush, PathGradientBrush, SolidBrush, and TextureBrush. The Graphics.DrawRectangle method requires a Brush object as one of its parameters; however, you will never pass the base Brush class to Graphics.DrawRectangle. Instead, you will pass one of the derived classes. Because they are each derived from the Brush class, the Graphics.DrawRectangle method can accept any of them. Similarly, if you were to create a custom class derived from the Brush class, you could also pass that class to Graphics.DrawRectangle.

什么是接口?

接口, 也就是常说的合同( contracts), 定义一组通用成员,所有的实现类都必须实现这些成员. For example, the IComparable interface defines the CompareTo method, which enables two instances of a class to be compared for equality. All classes that implement the IComparable interface, whether custom-created or built in the .NET Framework, can be compared for equality.

IDisposable is an interface that provides a single method, Dispose, to enable assemblies that create an instance of your class to free up any resources the instance has consumed. To create a class that implements the IDisposable interface using Visual Studio 2005, follow these steps:

  1. Create the class declaration. For example:

    ' VB
    Class BigClass
    End Class

    // C#
    class BigClass
    {
    }
  2. Add the interface declaration. For example:

    ' VB
    Class BigClass
    Implements IDisposable
    End Class

    // C#
    class BigClass : IDisposable
    {
    }
  3. If you are using Visual Basic, Visual Studio should automatically generate method declarations for each of the required methods. If it does not, delete the Implements command and try again; Visual Studio may still be starting up. 如果你使用C#, 右键点击 Interface 声明, 点击 “Implement Interface”。

  4. Write code for each of the interface's methods. In this example, you would write code in the Dispose method to deallocate any resources you had allocated.


Figure 1-1: Visual Studio simplifies implementing an interface

Table 1-6 lists the most commonly used interfaces in the .NET Framework.

Table 1-6: Commonly used interfaces

Class

Description

IComparable

Implemented by types whose values can be ordered; for example, the numeric and string classes. IComparable is required for sorting.

IDisposable

Defines methods for manually disposing of an object. This interface is important for large objects that consume resources, or objects that lock access to resources such as databases.

IConvertible

Enables a class to be converted to a base type such as Boolean, Byte, Double, or String.

ICloneable

Supports copying an object.

IEquatable

Allows you to compare to instances of a class for equality. For example, if you implement this interface, you could say "if (a = = b)".

IFormattable

Enables you to convert the value of an object into a specially formatted string. This provides greater flexibility than the base ToString method.

你也可以创建自己的接口. This is useful if you need to create multiple custom classes that behave similarly and can be used interchangeably. For example, the following code defines an interface containing three members:

' VB
Interface IMessage
' Send the message. Returns True is success, False otherwise.
Function Send() As Boolean
' The message to send.
Property Message() As String
' The Address to send to.
Property Address() As String
End Interface

// C#
interface IMessage
{

// Send the message. Returns True is success, False otherwise.
bool Send();
// The message to send.
string Message { get; set; }
// The Address to send to.
string Address { get; set; }
}

如果你在一个新的类里面实现接口,Visual Studio会自动生成如下的模板:

' VB
Class EmailMessage
Implements IMessage

Public Property Address() As String Implements IMessage.Address
Get
End Get
Set(ByVal value As String)
End Set
End Property

Public Property Message() As String Implements IMessage.Message
Get
End Get
Set(ByVal value As String)
End Set
End Property

Public Function Send() As Boolean Implements IMessage.Send
End Function
End Class

// C#
class EmailMessage : IMessage
{
public bool Send()
{
throw new Exception("The method or operation is not implemented.");
}

public string Message
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}

public string Address
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}
}

如果你自定义了一个类,之后,你发现这个类对其它类很有用,想提取这个类的方法来做接口,Visual Studio有一个简单的方法来做这个事情:

  1. 在Visual Studio 2005里面,右击 class.

  2. 点击 Refactor (重构) ,然后再点击 Extract Interface (提取接口) .

  3. 指定一个接口名字 (interface name), 选择 需要提取到接口的公共成员(public members), 然后点击 OK.

 一个类可以同时实现多个接口. Therefore, a class could implement both the IComparable and IDisposable interfaces.

什么是 Partial Classes (这个中文名字叫“局部类”?)?

  .NET 2.0 

Partial classes 是在 .NET 2.0新增的.

Partial classes 允许你把一个类拆分到多个源文件当中. The benefit of this approach is that it hides details of the class definition so that derived classes can focus on more significant portions.

The Windows Form class is an example of a built-in partial class. In Visual Studio 2003 and earlier, forms classes included code generated by the form designer. Now that code is hidden in a partial class named form.Designer.vb or form.Designer.cs.

In Visual Basic, you must select Show All Files in the Solution Explorer to see the partial class files. In C#, that view is enabled by default. Partial classes aren't part of the exam objectives, but you need to know about them so that you can find the Form Designer code when you create a new Windows Form.

什么是泛型 (Generics)?

Generics 是 .NET Framework's 类型系统的一部分, 允许你定义一个类型时不指定某些详细信息. Instead of specifying the types of parameters or member classes, you can allow code that uses your type to specify it. This allows consumer code to tailor your type to its own specific needs.

  Exam Tip 

Generic types are new in .NET 2.0, and you will probably see an unusually large number of questions about generics on the exam.

.NET 2.0 在 System.Collections.Generic 命名空间下,包含几种泛型类, 包含Dictionary, Queue, SortedDictionary, and SortedList. 这些类跟他们在System.Collections下的非泛型类工作是很类似的, 但是他们提高了效率并且类型安全.

  More Info—Generic collections 

The .NET Framework version 2.0 includes the System.Collections.Generic namespace, which provides built-in collections that offer improved performance over standard collections. For more information, refer to Chapter 4, "Collections and Generics."

为什么使用 Generics?

.NET1.0,1.1 不支持 generics. Instead,developers used the Object class for parameters and members and would cast other classes to and from the Object class. Generics offer two significant advantages over using the Object class:

  • Reduced run-time errors The compiler cannot detect type errors when you cast to and from the Object class. For example, if you cast a string to an Object class and then attempt to cast that Object to an integer, the compiler will not catch the error. Instead, the runtime will throw an exception. Using generics allows the compiler to catch this type of bug before your program runs. Additionally, you can specify constraints to limit the classes used in a generic, enabling the compiler to detect an incompatible type.

  • Improved performance Casting requires boxing and unboxing (explained later in Lesson 4, "Converting Between Types"), which steals processor time and slows performance. Using generics doesn't require casting or boxing, which improves run-time performance.

 

如何创建一个泛型类型?

First, examine the following classes. Classes Obj and Gen perform exactly the same tasks, but Obj uses the Object class to enable any type to be passed, while Gen uses generics:

' VB
Class Obj
Public V1 As Object
Public V2 As Object

Public Sub New(ByVal _V1 As Object, ByVal _V2 As Object)
V1 = _V1
V2 = _V2
End Sub
End Class

Class Gen(of T, U)
Public V1 As T
Public V2 As U

Public Sub New(ByVal _V1 As T, ByVal _V2 As U)
V1 = _V1
V2 = _V2
End Sub
End Class

// C#
class Obj
{
public Object t;
public Object u;

public Obj(Object _t, Object _u)
{
t = _t;
u = _u;
}
}

class Gen<T, U>
{
public T t;
public U u;

public Gen(T _t, U _u)
{
t = _t;
u = _u;
}
}

As you can see, the Obj class has two members of type Object. The Gen class has two members of type T and U. The consuming code will determine the types for T and U. Depending on how the consuming code uses the Gen class, T and U could be a string, an int, a custom class, or any combination thereof.

There is a significant limitation to creating a generic class: generic code is valid only if it will compile for every possible constructed instance of the generic, whether an Int, a string, or any other class. Essentially, you are limited to the capabilities of the base Object class when writing generic code. Therefore, you could call the ToString or GetHashCode method within your class, but you could not use the + or > operator. These same restrictions do not apply to the consuming code because the consuming code has declared a type for the generic.

如何使用泛型( Generic Type)?

当你使用泛型时, 你必须指定已经使用的任何一个泛型的具体类型. 请考虑下面控制台应用程序代码:

' VB
' Add two Strings using the Obj class
Dim oa As Obj = New Obj("Hello, ", "World!")
Console.WriteLine(CType(oa.V1, String) + CType(oa.V2, String))

' Add two Strings using the Gen class
Dim ga As New Gen(Of String, String)("Hello, ", "World!")
Console.WriteLine(ga.V1 + ga.V2)

' Add a Double and an Integer using the Obj class
Dim ob As Obj = New Obj(10.125, 2005)
Console.WriteLine(CType(ob.V1, Double) + CType(ob.V2, Integer))

' Add a Double and an Integer using the Gen class
Dim gb As New Gen(Of Double, Integer)(10.125, 2005)
Console.WriteLine(gb.V1 + gb.V2)

// C#
// Add two strings using the Obj class
Obj oa = new Obj("Hello, ", "World!");
Console.WriteLine((string)oa.t + (string)oa.u);

// Add two strings using the Gen class
Gen<string, string> ga = new Gen<string, string>("Hello, ", "World!");
Console.WriteLine(ga.t + ga.u);

// Add a double and an int using the Obj class
Obj ob = new Obj(10.125, 2005);
Console.WriteLine((double)ob.t + (int)ob.u);

// Add a double and an int using the Gen class
Gen<double, int> gb = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gb.t + gb.u);

If you run that code in a console application, the Obj and Gen classes produce exactly the same results. However, the code that uses the Gen class actually works faster because it does not require boxing and unboxing to and from the Object class. Additionally, developers would have a much easier time using the Gen class. First, developers would not have to manually cast from the Object class to the appropriate types. Second, type errors would be caught at compile time rather than at run time. To demonstrate that benefit, consider the following code, which contains an error:

' VB
' Add a Double and an Integer using the Gen class
Dim gb As New Gen(Of Double, Integer)(10.125, 2005)
Console.WriteLine(gb.V1 + gb.V2)

' Add a Double and an Integer using the Obj class
Dim ob As Obj = New Obj(10.125, 2005)
Console.WriteLine(CType(ob.V1, Integer) + CType(ob.V2, Integer))

// C#
// Add a double and an int using the Gen class
Gen<double, int> gc = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gc.t + gc.u);

// Add a double and an int using the Obj class
Obj oc = new Obj(10.125, 2005);
Console.WriteLine((int)oc.t + (int)oc.u);

The last line in that code sample contains an error—the oc.t value is cast to an Int instead of to a double. Unfortunately, the compiler won't catch the mistake. Instead, in C#, a run-time exception is thrown when the runtime attempts to cast a double to an Int value. In Visual Basic, which allows narrowing conversions by default, the result is even worse-a miscalculation occurs. It's much easier to fix a bug that the compiler catches and much harder to detect and fix a run-time error, so the generic class provides a clear benefit.

如何使用约束条件( Constraints)?

Generics would be extremely limited if you could only write code that would compile for any class, because you would be limited to the capabilities of the base Object class. To overcome this limitation, use constraints to place requirements on the types that consuming code can substitute for your generic.

Generics support four types of constraints:

  • Interface Allow only types that implement specific interfaces to use your generic.

  • Base class Allow only types that match or inherit from a specific base class to use your generic.

  • Constructor Require types that use your generic to implement a parameterless constructor.

  • Reference or value type Require types that use your generic to be either a reference or value type.

Use the As clause in Visual Basic or the where clause in C# to apply a constraint to a generic. For example, the following generic class could be used only by types that implement the IComparable interface:

' VB
Class CompGen(Of T As IComparable)
Public t1 As T
Public t2 As T

Public Sub New(ByVal _t1 As T, ByVal _t2 As T)
t1 = _t1
t2 = _t2
End Sub

Public Function Max() As T
If t2.CompareTo(t1) < 0 Then
Return t1
Else
Return t2
End If
End Function
End Class

// C#
class CompGen<T>
where T : IComparable
{
public T t1;
public T t2;


public CompGen(T _t1, T _t2)
{
t1 = _t1;
t2 = _t2;
}

public T Max()
{
if (t2.CompareTo(t1) < 0)
return t1;
else
return t2;
}
}

The preceding class will compile correctly. However, if you remove the where clause, the compiler will return an error indicating that generic type T does not contain a definition for CompareTo. By constraining the generic to classes that implement IComparable, you guarantee that the CompareTo method will always be available.


事件

Most projects are nonlinear. In Windows Forms applications, you might have to wait for a user to click a button or press a key, and then respond to that event. In server applications, you might have to wait for an incoming network request. These capabilities are provided by events in the .NET Framework, as described in the following sections.

什么是事件( Event)?

An event is a message sent by an object to signal the occurrence of an action. The action could be caused by user interaction, such as a mouse click, or it could be triggered by some other program logic. The object that raises the event is called the event sender. The object that captures the event and responds to it is called the event receiver.

In event communication, the event sender class does not know which object or method will receive (handle) the events it raises. What is needed is an intermediary (or pointer-like mechanism) between the source and the receiver. The .NET Framework defines a special type (Delegate) that provides the functionality of a function pointer.

什么是委托(Delegate)?

A delegate is a class that can hold a reference to a method. Unlike other classes, a delegate class has a signature, and it can hold references only to methods that match its signature. A delegate is thus equivalent to a type-safe function pointer or a callback. While delegates have other uses, the discussion here focuses on the event-handling functionality of delegates. A delegate declaration is sufficient to define a delegate class. The declaration supplies the signature of the delegate, and the common language runtime provides the implementation. The following example shows an event delegate declaration:

' VB
Public Delegate Sub AlarmEventHandler(sender As Object, e As EventArgs)

// C#
public delegate void AlarmEventHandler(object sender, EventArgs e);

The standard signature of an event handler delegate defines a method that does not return a value, whose first parameter is of type Object and refers to the instance that raises the event, and whose second parameter is derived from type EventArgs and holds the event data. If the event does not generate event data, the second parameter is simply an instance of EventArgs. Otherwise, the second parameter is a custom type derived from EventArgs and supplies any fields or properties needed to hold the event data.

EventHandler is a predefined delegate that specifically represents an event handler method for an event that does not generate data. If your event does generate data, you must supply your own custom event data type and either create a delegate where the type of the second parameter is your custom type, or you must use the generic EventHandler delegate class and substitute your custom type for the generic type parameter.

To associate the event with the method that will handle the event, add an instance of the delegate to the event. The event handler is called whenever the event occurs, unless you remove the delegate.

如何响应一个事件?

只需要做两件事:

  • Create a method to respond to the event. The method must match the Delegate signature. Typically, this means it must return void and accept two parameters: an Object and an EventArgs (or a derived class). The following code demonstrates this:

    ' VB
    Public Sub Button1_Click(sender As Object, e As EventArgs)
    ' Method code
    End Sub

    // C#
    private void button1_Click(object sender, EventArgs e)
    {
    // Method code
    }
  • Add the event handler to indicate which method should receive events, as the following code demonstrates:

    ' VB
    AddHandler Me.Button1.Click, AddressOf Me.Button1_Click

    // C#
    this.button1.Click += new System.EventHandler(this.button1_Click);
  .NET 2.0 

The .NET Framework 2.0 includes a new generic version of the EventHandler type.

When the event occurs, the method you specified will run.

How to Raise an Event

At a minimum, you must do three things to raise an event:

  • Create a delegate:

    ' VB
    Public Delegate Sub MyEventHandler(ByVal sender As Object, ByVal e As EventArgs)

    // C#
    public delegate void MyEventHandler(object sender, EventArgs e);
  • Create an event member:

    ' VB
    Public Event MyEvent As MyEventHandler

    // C#
    public event MyEventHandler MyEvent;
  • Invoke the delegate within a method when you need to raise the event, as the following code demonstrates:

    ' VB
    Dim e As EventArgs = New EventArgs
    RaiseEvent MyEvent(Me, e)

    // C#
    MyEventHandler handler = MyEvent;
    EventArgs e = new EventArgs();

    if (handler != null)
    {
    // Invokes the delegates.
    handler(this, e);
    }
    // Note that C# checks to determine whether handler is null.
    // This is not necessary in Visual Basic

Additionally, you can derive a custom class from EventArgs if you need to pass information to the event handler.

  Differences in raising events in Visual Basic and C# 

Visual Basic and C# differ when raising events. In C#, you must check whether the event is null before calling it. In Visual Basic, you can omit that check.

What Are Attributes?

Attributes describe a type, method, or property in a way that can be programmatically queried using a technique called Reflection. Some common uses for attributes are to

  • Specify which security privileges a class requires

  • Specify security privileges to refuse to reduce security risk

  • Declare capabilities, such as supporting serialization

  • Describe the assembly by providing a title, description, and copyright notice

Attribute types derive from the System.Attribute base class and are specified using <> or [] notation. The following code sample demonstrates how to add assembly attributes:

' VB AssemblyInfo.vb
<Assembly: AssemblyTitle("ch01vb")>
<Assembly: AssemblyDescription("Chapter 1 Samples")>
<Assembly: AssemblyCompany("Microsoft Learning")>
<Assembly: AssemblyProduct("ch01vb")>
<Assembly: AssemblyCopyright("Copyright © 2006")>
<Assembly: AssemblyTrademark("")>

// C# - AssemblyInfo.cs
[assembly: AssemblyTitle("ch01cs")]
[assembly: AssemblyDescription("Chapter 1 Samples")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft Learning")]
[assembly: AssemblyProduct("ch01cs")]
[assembly: AssemblyCopyright("Copyright © 2006")]
[assembly: AssemblyTrademark("")]

Visual Studio automatically creates some standard attributes for your assembly when you create a project, including a title, description, company, guide, and version. You should edit these attributes for every project you create because the defaults do not include important information such as the description.

Attributes do more than describe an assembly to other developers, they can also declare requirements or capabilities. For example, to enable a class to be serialized, you must add the Serializable attribute, as the following code demonstrates:

' VB
<Serializable()> Class ShoppingCartItem
End Class

// C#
[Serializable]
class ShoppingCartItem
{
}

Without the Serializable attribute, a class is not serializable. Similarly, the following code uses attributes to declare that it needs to read the C:\boot.ini file. Because of this attribute, the runtime will throw an exception prior to execution if it lacks the specified privilege:

' VB
Imports System.Security.Permissions

<Assembly: FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read := "C:\boot.ini")>
Module Module1
Sub Main()
Console.WriteLine("Hello, World!")
End Sub
End Module

// C#
using System;
using System.Security.Permissions;

[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:\boot.ini")]
namespace DeclarativeExample
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}

What Is Type Forwarding?

Type forwarding is an attribute (implemented in TypeForwardedTo) that allows you to move a type from one assembly (assembly A) into another assembly (assembly B), and to do so in such a way that it is not necessary to recompile clients that consume assembly A. After a component (assembly) ships and is being used by client applications, you can use type forwarding to move a type from the component (assembly) into another assembly and ship the updated component (and any additional assemblies required), and the client applications will still work without being recompiled. Type forwarding works only for components referenced by existing applications. When you rebuild an application, there must be appropriate assembly references for any types used in the application.

To move a type from one class library to another, follow these steps:

  .NET 2.0 

Type forwarding is a new feature in .NET 2.0.

  1. Add a TypeForwardedTo attribute to the source class library assembly.

  2. Cut the type definition from the source class library.

  3. Paste the type definition into the destination class library.

  4. Rebuild both libraries.

The following code shows the attribute declaration used to move TypeA to the DestLib class library:

' VB
Imports System.Runtime.CompilerServices
<Assembly:TypeForwardedTo(GetType(DestLib.TypeA))]>

// C#
using System.Runtime.CompilerServices;
[assembly:TypeForwardedTo(typeof(DestLib.TypeA))]

Lab: Create a Derived Class with Delegates

The following exercises demonstrate inheritance and events. If you encounter a problem completing an exercise, the completed projects are available on the companion CD in the Code folder.

Exercise 1: Derive a New Class from an Existing Class
Image from book

In this exercise, you will derive a new class from the Person class you created in Lesson 1.

  1. Copy the Chapter01\Lesson3-Person folder from the companion CD to your hard disk, and open either the C# version or the Visual Basic version of the CreateStruct project.

  2. Change the Person structure to a class.

  3. Create a new class definition named Manager that inherits from the base Person class.

    ' VB
    Class Manager
    End Class

    // C#
    class Manager : Person
    {
    }
  4. Add two new public members as strings: phoneNumber and officeLocation.

  5. Override the constructor to accept a phone number and office location to define the new members. You will need to call the base class's constructor, as shown in the following code sample:

    ' VB
    Public Sub New(ByVal _firstName As String, ByVal _lastName As String, _
    ByVal _age As Integer, ByVal _gender As Genders, ByVal _phoneNumber As String, _
    ByVal _officeLocation As String)
    MyBase.New(_firstName, _lastName, _age, _gender)
    phoneNumber = _phoneNumber
    officeLocation = _officeLocation
    End Sub

    // C#
    public Manager(string _firstName, string _lastName, int _age,
    Genders _gender, string _phoneNumber, string _officeLocation)
    : base (_firstName, _lastName, _age, _gender)
    {
    phoneNumber = _phoneNumber;
    officeLocation = _officeLocation;
    }
  6. Override the ToString method to add the phone number and office location, as shown in the following sample:

    ' VB
    Public Overloads Overrides Function ToString() As String
    Return MyBase.ToString + ", " + phoneNumber + ", " + officeLocation
    End Function

    // C#
    public override string ToString()
    {
    return base.ToString() + ", " + phoneNumber + ", " + officeLocation;
    }
  7. Modify the Main method to create a Manager object instead of a person object. Then run your application to verify that it works correctly.

Image from book
 
Exercise 2: Respond to an Event
Image from book

In this exercise, you will create a class that responds to a timer event.

  1. Using Visual Studio, create a new Windows Forms application project. Name the project TimerEvents.

  2. Add a ProgressBar control to the form, as shown in Figure 1-2.

  3. Within the form class declaration, declare an instance of a System.Windows.Forms.Timer object. Timer objects can be used to throw events after a specified number of milliseconds. The following code sample shows how to declare a Timer object:

    ' VB
    Dim t As Timer

    // C#
    System.Windows.Forms.Timer t;
  4. In the designer, view the properties for the form. Then view the list of events. Double-click the Load event to automatically create an event handler that will run the first time the form is initialized. Within the method, initialize the Timer object, set the interval to one second, create an event handler for the Tick event, and start the timer. The following code sample demonstrates this:

    ' VB
    Private Sub Form1_Shown(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles MyBase.Shown
    t = New System.Windows.Forms.Timer
    t.Interval = 1000
    AddHandler t.Tick, AddressOf Me.t_Tick
    t.Start()
    End Sub

    // C#
    private void Timer_Shown(object sender, EventArgs e)
    {
    t = new System.Windows.Forms.Timer();
    t.Interval = 1000;
    t.Tick += new EventHandler(t_Tick);
    t.Start();
    }
  5. Implement the method that will respond to the Timer.Tick event. When the event occurs, add 10 to the ProgressBar.Value attribute. Then stop the timer if the ProgressBar.Value attribute has reached 100. The following code sample demonstrates this:

    ' VB
    Private Sub t_Tick(ByVal sender As Object, ByVal e As EventArgs)
    ProgressBar1.Value += 10

    If ProgressBar1.Value = 100 Then
    t.Stop()
    End If
    End Sub

    // C#
    void t_Tick(object sender, EventArgs e)
    {
    progressBar.Value += 10;

    if (progressBar.Value >= 100)
    t.Stop();
    }
  6. Run the application to verify that it responds to the timer event every second.

 

Image from book
Figure 1-2: You will control this progress bar by responding to timer events
Image from book
 

Lesson Summary

  • Use inheritance to create new types based on existing ones.

  • Use interfaces to define a common set of members that must be implemented by related types.

  • Partial classes split a class definition across multiple source files.

  • Events allow you to run a specified method when something occurs in a different section of code.

  • Use attributes to describe assemblies, types, and members.

  • Use the TypeForwardedTo attribute to move a type from one class library to another.

Lesson Review

You can use the following questions to test your knowledge of the information in Lesson 3, "Constructing Classes." The questions are also available on the companion CD if you prefer to review them in electronic form.

  Answers 

Answers to these questions and explanations of why each answer choice is right or wrong are located in the "Answers" section at the end of the book.

1. 

Which of the following statements are true? (Choose all that apply.)

  1. Inheritance defines a contract between types.

  2. Interfaces define a contract between types.

  3. Inheritance derives a type from a base type.

  4. Interfaces derive a type from a base type.

Image from book

2. 

Which of the following are examples of built-in generic types? (Choose all that apply.)

  1. Nullable

  2. Boolean

  3. EventHandler

  4. System.Drawing.Point

Image from book

3. 

You are creating a generic class, and you need to dispose of the generic objects. How can you do this?

  1. Call the Object.Dispose method.

  2. Implement the IDisposable interface.

  3. Derive the generic class from the IDisposable class.

  4. Use constraints to require the generic type to implement the IDisposable interface.

Image from book

4. 

You've implemented an event delegate from a class, but when you try to attach an event procedure you get a compiler error that there is no overload that matches the delegate. What happened?

  1. The signature of the event procedure doesn't match that defined by the delegate.

  2. The event procedure is declared Shared/static, but it should be an instance member instead.

  3. You mistyped the event procedure name when attaching it to the delegate.

  4. The class was created in a different language.

Image from book

Answers

1. 

Correct Answers: B and C

  1. Incorrect: Interfaces define a contract between types; inheritance derives a type from a base type.

  2. Correct: Interfaces define a contract between types, ensuring that a class implements specific members.

  3. Correct: Inheritance derives a type from a base type, automatically implementing all members of the base class, while allowing the derived class to extend or override the existing functionality.

  4. Incorrect: Interfaces define a contract between types; inheritance derives a type from a base type.

2. 

Correct Answers: A and C

  1. Correct: Nullable is a generic type.

  2. Incorrect: Boolean is a nongeneric value type.

  3. Correct: EventHandler is a generic type.

  4. Incorrect: System.Drawing.Point is a structure and is not generic.

3. 

Correct Answer: D

  1. Incorrect: The Object class does not have a Dispose member method. Additionally, you would need to use a constraint to mandate types implementing the IDisposable interface to call the Dispose method.

  2. Incorrect: Implementing an interface does not enable generic types to use interface methods.

  3. Incorrect: Deriving the generic class from an interface does not enable generic types to use interface methods.

  4. Correct: If you use constraints to require types to implement a specific interface, you can call any methods used in the interface.

4. 

Correct Answer: A

  1. Correct: Delegates define the signature (arguments and return type) for the entry point.

  2. Incorrect: Event procedures can be Shared/static or instance members.

  3. Incorrect: If you mistyped the event procedure name, you would receive a different compiler error.

  4. Incorrect: Events work equally well, regardless of the language used.

posted @ 2008-09-18 17:00  无尽思绪  阅读(495)  评论(0编辑  收藏  举报