C# ПІДРУЧНИКИ / c# / MS Press - Oop With MS Vb Net And C# Step By Step (2002)
.pdfthe icon from the assembly. That’s a lot of classes and method calls to match up, so let’s look at the code to see how it works out. The following code shows the basic process for retrieving the Hearts.ico icon:
‘ Visual Basic
Dim theAssembly As System.Reflection.Assembly
theAssembly = System.Reflection.Assembly.GetExecutingAssembly()
Dim assemblyName As String = theAssembly.GetName().Name
Dim resourceName As String = assemblyName & ".Hearts.ico"
Dim iconStream As System.IO.Stream = _
theAssembly.GetManifestResourceStream(resourceName)
Dim theIcon As Icon = new Icon(iconStream)
// Visual C#
System.Reflection.Assembly assembly;
assembly = System.Reflection.Assembly.GetExecutingAssembly();
string assemblyName = assembly.GetName().Name;
string resourceName = assemblyName + ".Hearts.ico";
System.IO.Stream iconStream =
theAssembly.GetManifestResourceStream(resourceName);
Icon theIcon = new Icon(iconStream);
1.In Solution Explorer, right-click Card.cs or Card.vb and click View Code on the shortcut menu.
2.Modify the Card class default constructor—the constructor that doesn’t take arguments—to delete the calls to add icons to the m_icons SortedList. If you’re using Visual Basic you’ll find the constructor in the region labeled Windows Form Designer Generated Code. The constructor after modification is shown here:
3.‘ Visual Basic
4.Public Sub New()
5.MyBase.New()
6.‘This call is required by the Windows Form Designer.
7.InitializeComponent()
8.End Sub
9.
10.// Visual C#
11.public Card()
12.{
13.// This call is required by the Windows.Forms Form Designer.
14.InitializeComponent();
}
15.If you’re using Visual Basic, add an Imports statement at the top of the source file for the System.ComponentModel namespace. The Card class you defined in Chapter 8 was part of a Class Library project, and the System.
Component namespace was a project-wide import. The namespace isn’t imported by default in a Visual Basic Windows application and must be added. If you’re using Visual C#, the using statement for System.ComponentModel is already in the Card.cs file.
16.‘ Visual Basic
Imports System.ComponentModel
17.Modify the declaration of the SortedList field so that it’s a shared or static data member.
18.‘ Visual Basic
19.Shared m_images As SortedList = New SortedList()
20.
21.// Visual C#
static SortedList m_images = new SortedList();
22.Add the shared or static constructor to fill the m_icons SortedList with the embedded icons. Like shared and static methods, the shared or static constructor can use only shared or static data.
23.‘ Visual Basic
24.Shared Sub New()
25.Dim theAssembly As System.Reflection.Assembly
26.theAssembly = System.Reflection.Assembly.GetExecutingAss embly()
27.Dim assemblyName As String = theAssembly.GetName().Nam
e
28.
29.Dim iconStream As System.IO.Stream
30.Dim resourceName As String
31.Dim theIcon As Icon
32.Dim theSuit As Object
33.Dim aSuit As Integer
34.Dim suitNames() As String =
35.System.Enum.GetNames(System.Type.GetType("BetterCar
d.Suit"))
36.For aSuit = 0 To suitNames.Length - 1
37.resourceName = assemblyName & "." & suitNames(aSuit) &
".ico"
38.iconStream = theAssembly.GetManifestResourceStream(res ourceName)
39.theIcon = new Icon(iconStream)
40.theSuit = System.Enum.Parse( _
41.System.Type.GetType("BetterCard.Suit"), suitNames(aSui
t))
42.m_images.Add(theSuit, theIcon)
43.Next
44.End Sub
45.
46.// Visual C#
47.static Card() {
48.System.Reflection.Assembly assembly;
49.assembly = System.Reflection.Assembly.GetExecutingAssemb ly();
50. string assemblyName = assembly.GetName().Name;
51.
52.System.IO.Stream iconStream;
53.string resourceName;
54.Icon theIcon;
55.object theSuit;
56.string[] suitNames = Enum.GetNames(typeof(Suit));
57.for (int aSuit = 0; aSuit < suitNames.Length; aSuit++) {
58.resourceName = assemblyName + "." + suitNames[aSuit] +
".ico";
59.iconStream = assembly.GetManifestResourceStream(resour ceName);
60.theIcon = new Icon(iconStream);
61.theSuit = Enum.Parse(typeof(Suit),suitNames[aSuit],true);
62.m_images.Add(theSuit, theIcon);
63.}
}
This constructor uses a generalized version of the code snippet shown on page 304 to collect the four icons. The constructor uses the shared or static GetNames and Parse methods of the System.Enum class. The Enum class provides several methods for manipulating enumerations. The GetName method returns an array of strings with the names of the enumeration members. You use this array to create the names of the icon files in the assembly. (Remember that you carefully named the icon files to match the enumeration names.) You then use the Enum.Parse method to return a Suit enumeration value to use as the key into the SortedList.
The shared or static constructor is called only once during an application’s lifetime—some time after the application starts, but before the first instance of the class is created. Client code can’t call the static constructor, meaning that the developer can’t control when the constructor is called.
64.In the Paint event handler, Card_Paint, remove the Me or this scoping operator from the m_images reference. The m_images field is no longer instance data, and using the this operator isn’t allowed, because the static m_images field isn’t associated with a particular instance of Card. Visual Basic allows the reference to Me.m_images, but it’s good practice and less confusing to someone readying the code to remove the Me. The code to remove is shown in bold.
65.‘ Visual Basic
66.g.DrawIcon(CType(Me.m_images(m_suit), Icon), 14, 40)
67.
68.// Visual C#
g.DrawIcon((Icon)(this. m_images[m_suit]), 14, 40); 69. From the Build menu, select Build Solution.
Test the Card class
In testing the Card class, you’ll create a form that has one card and two ListBox controls. At run time, the ListBox controls will contain Suit and FaceValue enumeration values. As you select a new value, the Card will reflect the new value. You’ll use a shared or static member of the Enum class to retrieve the values of the Suit and FaceValue enumerations.
1.Open Form1 in the form designer.
2.Add controls and set their properties as shown in the following table. You’ll find the Card control in the Toolbox. Arrange the controls
however you like. The suitList control will contain a list of the Suit enumeration values and the faceValueList will contain a list of the FaceValue enumerations.
|
|
|
|
|
|
|
|
Control |
|
Property |
|
Value |
|
|
|
|
|
|
|
|
|
Card |
|
Name |
|
card1 |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
FaceUp |
|
True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ListBox |
|
Name |
|
suitList |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
ListBox |
|
Name |
|
faceValueList |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
16.Double-click on the form to create the Load event handler. Add the following code to fill the ListBox controls:
17.‘ Visual Basic
18.Private Sub Form1_Load(ByVal sender As System.Object, _
19.ByVal e As System.EventArgs) Handles MyBase.Load
20.suitList.DataSource = _
21.System.Enum.GetValues(System.Type.GetType("BetterCar
d.Suit"))
22.faceValueList.DataSource = _
23.System.Enum.GetValues(FaceValue.Queen.GetType())
24.End Sub
25.
26.// Visual C#
27.private void Form1_Load(object sender, System.EventArgs e) {
28.suitList.DataSource = Enum.GetValues(typeof(Suit));
29.faceValueList.DataSource = Enum.GetValues(typeof(FaceValu
e));
}
The Enum.GetValues method takes a Type parameter representing the enumeration and returns the values of the enumeration in an Array instance. Each member of the Array instance is an enumeration value. The Type class is a .NET Framework class that represents classes defined in an application. Before you can call the Enum.GetValues method, you need to get a Type instance representing the enumeration. The typeof operator of Visual C# returns the Type instance representing a defined type. To retrieve the Type instance in Visual Basic, you can use the GetType method on a particular enumeration value, or the shared Type.GetType method. An example of each is used in the code.
30.In the form designer, double-click the suitList list box to create the SelectedIndexChanged event handler. The SelectedItem property of the ListBox control returns a System.Object instance that must be cast back to Suit to be used as the Suit property of the card1 object.
31.‘ Visual Basic
32.Private Sub suitList_SelectedIndexChanged(ByVal sender As Sy stem.Object, _
33.ByVal e As System.EventArgs) Handles suitList.SelectedIndexCh anged
34.Me.card1.Suit = CType(Me.suitList.SelectedItem, Suit)
35.End Sub
36.
37.// Visual C#
38.private void suitList_SelectedIndexChanged(object sender,
39.System.EventArgs e) {
40.this.card1.Suit = (Suit) this.suitList.SelectedItem;
}
41.In the form designer, double-click the faceValueList list box to create the SelectedIndexChanged event handler.
42.‘ Visual Basic
43.Private Sub faceValueList_SelectedIndexChanged( _
44.ByVal sender As System.Object, ByVal e As System.EventArgs)
_
45.Handles faceValueList.SelectedIndexChanged
46.Me.card1.FaceValue = _
47.CType(Me.faceValueList.SelectedItem, FaceValue)
48.End Sub
49.
50.// Visual C#
51.private void faceValueList_SelectedIndexChanged(object sender,
52.System.EventArgs e) {
53.this.card1.FaceValue = (FaceValue) this.faceValueList.Selecte dItem;
}
54.Press F5 to run the program. Example output is shown below. As you select different Suit and FaceValues values in the ListBox controls, the appearance of the card changes.
The Singleton Pattern
One of the most well-known uses of shared and static members is to implement the Singleton design pattern. A design pattern is a solution to a common problem. The description of a pattern generally includes the pattern name, a description of the problem, a description of the solution, and an analysis of the consequences of using the pattern. The classic reference on design patterns is Design Patterns by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley, 1995). I’ll talk about design patterns again in Chapter 14, “Reducing Complexity by Design.”
Your application might have a constraint that only one instance of the class can be created. This is a common limitation when the class is an abstraction of a hardware or operating system component, such as a file manager or a print spooler. In this case, you want to implement the Singleton pattern, a well-known object-oriented design pattern.
Implement the Singleton pattern
The implementation of the Singleton pattern has these characteristics:
§The constructor is private so that no client code can create an instance. This allows the class to create and control access to the one instance.
§The single instance is available only through the shared or static method.
The essential implementation of the Singleton pattern is short and simple and is shown in the following steps.
1.Create a new Windows application. Name it Singleton.
2.From the Project menu, select Add New Item, and then select Code File from the list of templates. Name the new code file Singleton.
3.Add the following code to the code file to define the basic Singleton class. This implementation uses lazy initialization, meaning the instance isn’t created until the first time it’s retrieved.
4.‘ Visual Basic
5.Class Singleton
6.Shared m_instance As Singleton
7.Public Shared Function GetInstance() As Singleton
8.If (m_instance Is Nothing) Then
9.m_instance = New Singleton()
10.End If
11.Return m_instance
12.End Function
13.
14.Private Sub New()
15.End Sub
16.End Class
17.
18.// Visual C#
19.namespace Singleton {
20.class Singleton {
21.static Singleton m_instance;
22.public static Singleton GetInstance() {
23.if (m_instance == null) {
24. m_instance = new Singleton();
25.}
26.return m_instance;
27.}
28.private Singleton() {}
29.}
30.}
31.Add two member functions and a field to the class to store and return a collection of strings. You’ll use these methods to demonstrate that only one instance of the Singleton class is created. Note that m_list is instance data, not shared or static data.
32. ‘ Visual Basic
33.Dim m_list As System.Collections.ArrayList = _
34.New System.Collections.ArrayList()
35.Public Sub AddString(ByVal newString As String)
36.m_list.Add(newString)
37.End Sub
38.
39.Public Function GetStrings() As String()
40.Return CType(m_list.ToArray(System.Type.GetType("System. String")), _
41.String())
42.End Function
43.
44.// Visual C#
45.System.Collections.ArrayList m_list =
46.new System.Collections.ArrayList();
47.public void AddString(string newString) {
48.m_list.Add(newString);
49.}
50.
51.public string[] GetStrings() {
52.return (string[])m_list.ToArray(typeof(string));
}
Test the Singleton class
To demonstrate that there is only one instance of the Singleton class, you’ll use the array returned by the GetStrings method as the data source to two ListBox controls. When you add strings to one of the references, you’ll see the change propagated to both ListBox controls.
1.Open Form1 in the form designer and add controls and set their properties as shown in the following table. Arrange the controls as you like.
|
|
|
|
|
|
|
|
Control |
|
Property |
|
Value |
|
|
|
|
|
|
|
|
|
ListBox |
|
Name |
|
listOne |
|
|
|
|
|
|
|
|
|
ListBox |
|
Name |
|
listTwo |
|
|
|
|
|
|
|
|
|
TextBox |
|
Name |
|
newString |
|
|
|
|
|
|
|
|
|
|
|
Text |
|
(blank) |
|
|
|
|
|
|
|
|
|
Button |
|
Name |
|
addString |
|
|
|
|
|
|
|
|
|
|
|
Text |
|
Add |
|
|
|
|
|
|
String |
|
|
|
|
|
|
|
|
6.Double-click Form1 to create the form’s Load event. Add code to create the Singleton instance. Also add two fields for the Singleton references.
7.‘ Visual Basic
8.Dim singletonOne As Singleton
9.Dim singletonTwo As Singleton
10.
11.Private Sub Form1_Load(ByVal sender As System.Object, _
12.ByVal e As System.EventArgs) Handles MyBase.Load
13.‘ The following line won’t compile because there’s no
14.‘ public constructor.
15.‘ singletonOne = New Singleton()
16.singletonOne = Singleton.GetInstance()
17.singletonTwo = Singleton.GetInstance()
18.End Sub
19.
20.// Visual C#
21.Singleton singletonOne;
22.Singleton singletonTwo;
23.
24.private void Form1_Load(object sender, System.EventArgs e) {
25.// The following line won’t compile because there’s no
26.// public constructor.
27.// Singleton aSingleton = new Singleton();
28.singletonOne = Singleton.GetInstance();
29.singletonTwo = Singleton.GetInstance();
}
30.Create a Click event handler for the Add String button and add this code to demonstrate that both Singleton references, singletonOne and singletonTwo, refer to the same instance of Singleton.
31.‘ Visual Basic
32.Private Sub addString_Click(ByVal sender As System.Object, _
33.ByVal e As System.EventArgs) Handles addString.Click
34.singletonOne.AddString(newString.Text)
35.
36.listOne.DataSource = Nothing
37.listOne.Items.Clear()
38.listOne.DataSource = singletonOne.GetStrings()
40.listTwo.DataSource = Nothing
41.listTwo.Items.Clear()
42.listTwo.DataSource = singletonTwo.GetStrings()
43.End Sub
44.
45.// Visual C#
46.private void addString_Click(object sender, System.EventArgs e)
{
47.singletonOne.AddString(newString.Text);
48.
49.listOne.DataSource = null;
50.listOne.Items.Clear();
51.listOne.DataSource = singletonOne.GetStrings();
53.listTwo.DataSource = null;
54.listTwo.Items.Clear();
55.listTwo.DataSource = singletonTwo.GetStrings();
}
You could also add a test to the button that simply tests whether the references are the same:
‘ Visual Basic
If (singletonOne Is singletonTwo) And (Not IsNothing(singletonOne)) Then MessageBox.Show("They are the same.")
End If
// Visual C#
if ((singletonOne == singletonTwo) && (singletonOne != null)) { MessageBox.Show("They are the same.");
}
56.Press F5 to run the program. Add several strings and note that both list boxes have the same list of items, even though the code is only adding strings to the singletonOne reference. Example output is shown here:
Design Considerations
Shared and static members solve many programming tasks, but like any programming construct, they need to be used wisely. What follows are some tips and warnings about using shared and static members.
§Too many static members Since static members often track information about groups of instances, you might be tempted to add members that represent a group abstraction to the class. Rather than provide static properties TotalCars and AverageWeight for the Car class, you’re better off creating a ParkingLot class to maintain the data. In general, don’t complicate the design with unnecessary classes, but do make sure that each class represents one abstraction.
§When static properties become global data Using global data is, in general, a poor programming practice. When you make data global, you lose control of it. Global data can be passed to any method and then changed in unexpected ways. Public static data is available to any method in which the class is in scope. Used without planning, static data can easily become global data. Look closely at your design if you have a significant amount of public static data.
§Multithreaded applications If you’re working with a multithreaded application, you have to take synchronization into account. Suppose you have a class with a shared or static array of integers. You might have two instances of the class, on different threads, modifying the array. One instance might be able to complete only part of its modifications before the second instance starts modifying the data, leading to unexpected results. For information on synchronizing access to static variables, see the lock keyword in Visual C# and the SyncLock keyword in Visual Basic. The .NET
Framework documentation provides threading information on many classes.
Quick Reference
To
Create a shared or static field
Create a shared or static property
Create a shared or static method
Create a shared or static constructor
Call a shared or static member
Do this
Add the Shared or static keyword to the declaration.
‘ Visual Basic
Shared m_number As Integer
// Visual C#
static int m_number;
Add the Shared or static keyword to the declaration.
‘ Visual Basic
Public Shared Property Number() As Integer Get
Return m_number End Get
Set(ByVal Value As Integer) m_center = Value
End Set End Property
// Visual C#
public static int Number { get { return m_number; } set { m_number = value; }
}
Add the Shared or static keyword to the declaration.
‘ Visual Basic
Public Shared Sub SomeMethod() End Sub
// Visual C#
public static void SomeMethod() {
}
Add the Shared or static keyword to the declaration.
‘ Visual Basic Shared Sub New() End Sub
// Visual C# static Card() {
}
In Visual Basic, use the class name or an instance name.
In Visual C#, use the class name.
‘ Visual Basic
SomeClass.Number = 5
SomeClass.SomeMethod()
// Visual C# SomeClass.Number = 5; SomeClass.SomeMethod();
Chapter 12: Overloading Operators with Visual C#
