Implementing Type Conversion

It is important for a visual framework to support type conversion. For example, controls have graphical properties, but it is often difficult or impossible to show an image in the small space provided in the Properties window. For the Properties window the path to the image might be fine, but the GUI needs to display the actual image. This duality constitutes a type conversion of an image between a file path and the actual image. Other kinds of data need other kinds of conversion; for instance, a color may be rendered as text when serialized to a resource file, but the actual color is rendered in the GUI. Because various kinds of data conversions are needed to support the IDE and runtime applications, a general conversion mechanism is needed. .NET supplies two general conversion mechanisms.

If you define a new type, you can implement the IConvertible interface, which supports one-way, runtime conversion between a new type and an existing type. The mechanism for conversion—using the IConvertible interface—includes the shared methods in the System.Convert class. The process implements IConvertible, calls System.Convert, and uses members that make up the IConvertible interface to perform the conversion. For instance, System.Convert.ToBoolean tests an object to see if it implements IConvertible and then invokes the IConvertible.ToBoolean method. You aren't required to perform the conversion; simply provide the ToBoolean method. If it doesn't make sense to convert your object to a Boolean, raise an exception.

If you want to implement design time and runtime type conversion, you will want to implement a TypeConverter subclass. TypeConverter objects exist to support design time conversion, such as converting the file path of an image between the path and the image, depending on the context.

To demonstrate IConvertible and TypeConverter, I defined a new class, Circle, which supports what may be reasonable conversions for both runtime and design time type conversion. For instance, if we elect to use Circle as a property in a component, we may prefer to show the center point and radius values rather than try to render the circle in the Properties window. The subsections that follow contain the implementations of Circle, the conversion code, and some test code. (You can run and test the example code by downloading ConversionDemo.sln.)

Implementing the IConvertible Interface

Like all interfaces, IConvertible has specific members that we must implement. These methods are GetTypeCode, ToBoolean, ToByte, ToChar, ToDateTime, ToDecimal, ToDouble, ToInt16, ToInt32, ToByte, ToString, ToType, ToUInt16, ToUInt32, and TUInt64. You might recognize these as members of the System.Convert class.

In general, implement IConvertible under the following conditions:

  • When you need to perform conversions to general, system types using System.Convert

  • When you need to perform the conversion only at runtime

  • When you need to convert only from a new type to an existing type but not back again

Listing 9.13 contains a reasonable implementation of the Circle structure. Because we need a class in which to hold the interface members, the Circle structure itself implements the IConvertible interface.

Listing 9.13. Implementing the Circle Structure and the IConvertible Interface
1:  Imports System.ComponentModel
2:
3:  <TypeConverter(GetType(CircleConverter))> _
4:  Public Structure Circle
5:    Implements IConvertible
6:
7:    Private FCenter As Point
8:    Private FRadius As Single
9:
10:   Public Sub New(ByVal X As Integer, _
11:     ByVal Y As Integer, ByVal Radius As Single)
12:     FCenter = New Point(X, Y)
13:     FRadius = Radius
14:   End Sub
15:
16:   Public Sub New(ByVal Center As Point, _
17:     ByVal Radius As Single)
18:     FCenter = Center
19:     FRadius = Radius
20:   End Sub
21:
22:   Public Property Center() As Point
23:   Get
24:     Return FCenter
25:   End Get
26:   Set(ByVal Value As Point)
27:     FCenter = Value
28:   End Set
29:   End Property
30:
31:   Public ReadOnly Property Left() As Integer
32:   Get
33:     Return FCenter.X - FRadius  2
34:   End Get
35:   End Property
36:
37:   Public ReadOnly Property Top() As Integer
38:   Get
39:     Return FCenter.Y - FRadius  2
40:   End Get
41:   End Property
42:
43:   Public ReadOnly Property Radius() As Single
44:   Get
45:     Return FRadius
46:   End Get
47:   End Property
48:
49:   Public ReadOnly Property Height() As Integer
50:   Get
51:     Return Convert.ToInt32(FRadius)
52:   End Get
53:   End Property
54:
55:   Public ReadOnly Property Width() As Integer
56:   Get
57:     Return Convert.ToInt32(FRadius)
58:   End Get
59:   End Property
60:
61:   Public ReadOnly Property ToBoundingRectangle() _
62:     As Rectangle
63:   Get
64:     Return New Rectangle(Left, Top, Width, Height)
65:   End Get
66:   End Property
67:
68:   Public ReadOnly Property ToBoundingRectangleF() _
69:     As RectangleF
70:   Get
71:     Return New RectangleF(Left, Top, Width, Height)
72:   End Get
73:   End Property
74:
75:   Public ReadOnly Property Area() As Single
76:   Get
77:     Return Math.PI * Math.Pow(FRadius, 2)
78:   End Get
79:   End Property
80:
81:   Public ReadOnly Property Circumference() As Single
82:   Get
83:     Return 2 * Math.PI * FRadius
84:   End Get
85:   End Property
86:
87:   Public ReadOnly Property Diameter() As Single
88:   Get
89:     Return 2 * FRadius
90:   End Get
91:   End Property
92:
93:   Private Function GetTypeCode() As TypeCode _
94:     Implements IConvertible.GetTypeCode
95:     Return TypeCode.Object
96:   End Function
97:
98:   Private Function ToBoolean( _
99:     ByVal provider As IFormatProvider) As Boolean _
100:    Implements IConvertible.ToBoolean
101:
102:    ThrowException("conversion to Boolean not supported")
103:    Return False
104:  End Function
105:
106:  Private Function ToByte( _
107:    ByVal provider As IFormatProvider) As Byte _
108:    Implements IConvertible.ToByte
109:
110:    ThrowException("conversion to Byte not supported")
111:    Return 0
112:  End Function
113:
114:  Private Function ToChar( _
115:    ByVal provider As IFormatProvider) As Char _
116:    Implements IConvertible.ToChar
117:
118:    ThrowException("conversion to Char not supported")
119:    Return Chr(0)
120:  End Function
121:
122:  Private Function ToDateTime( _
123:    ByVal provider As IFormatProvider) As DateTime _
124:    Implements IConvertible.ToDateTime
125:
126:    ThrowException("conversion to DateTime not supported")
127:    Return DateTime.Now
128:  End Function
129:
130:  Private Function ToDecimal( _
131:    ByVal provider As IFormatProvider) As Decimal _
132:    Implements IConvertible.ToDecimal
133:
134:    ThrowException("conversion to Decimal not supported")
135:    Return Decimal.Zero
136:  End Function
137:
138:  Private Function ToDouble( _
139:    ByVal provider As IFormatProvider) As Double _
140:    Implements IConvertible.ToDouble
141:
142:    ThrowException("conversion to Double not supported")
143:    Return Double.MinValue
144:  End Function
145:
146:  Private Function ToInt16( _
147:    ByVal provider As IFormatProvider) As Int16 _
148:    Implements IConvertible.ToInt16
149:
150:    ThrowException("conversion to Int16 not supported")
151:    Return Int16.MinValue
152:  End Function
153:
154:  Private Function ToInt32( _
155:    ByVal provider As IFormatProvider) As Int32 _
156:    Implements IConvertible.ToInt32
157:
158:    ThrowException("conversion to Int32 not supported")
159:    Return Int32.MinValue
160:  End Function
161:
162:  Private Function ToInt64( _
163:    ByVal provider As IFormatProvider) As Int64 _
164:    Implements IConvertible.ToInt64
165:
166:    ThrowException("conversion to Int64 not supported")
167:    Return Int64.MinValue
168:  End Function
169:
170:  Private Function ToSingle( _
171:    ByVal provider As IFormatProvider) As Single _
172:    Implements IConvertible.ToSingle
173:
174:    ThrowException("conversion to Single not supported")
175:    Return Single.MinValue
176:
177:  End Function
178:
179:  Private Function ToUInt16( _
180:    ByVal provider As IFormatProvider) As UInt16 _
181:    Implements IConvertible.ToUInt16
182:
183:    ThrowException("conversion to UInt16 not supported")
184:    Return UInt16.Parse("0")
185:  End Function
186:
187:  Private Function ToUInt32( _
188:    ByVal provider As IFormatProvider) As UInt32 _
189:    Implements IConvertible.ToUInt32
190:
191:    ThrowException("conversion to UInt32 not supported")
192:    Return UInt32.Parse("0")
193:  End Function
194:
195:  Private Function ToUInt64( _
196:    ByVal provider As IFormatProvider) As UInt64 _
197:    Implements IConvertible.ToUInt64
198:
199:    ThrowException("conversion to UInt64 not supported")
200:    Return UInt64.Parse("0")
201:  End Function
202:
203:  Private Function ToSByte( _
204:    ByVal provider As IFormatProvider) As SByte _
205:    Implements IConvertible.ToSByte
206:
207:    ThrowException("conversion to SByte not supported")
208:    Return SByte.Parse("0")
209:  End Function
210:
211:  Private Overloads Function ToString( _
212:    ByVal provider As IFormatProvider) As String _
213:    Implements IConvertible.ToString
214:
215:    Return String.Format("{0},{1},{2}", _
216:      FCenter.X, FCenter.Y, FRadius.ToString())
217:
218:  End Function
219:
220:  Public Function ToType( _
221:    ByVal conversionType As Type, _
222:    ByVal provider As IFormatProvider) As Object _
223:    Implements IConvertible.ToType
224:
225:    If (conversionType Is GetType(String)) Then
226:      Return Me.GetType
227:    Else
228:      ThrowException(String.Format( _
229:        "conversion to {0} not 229: supported", _
230:        conversionType.ToString))
231:    End If
232:
233:  End Function
234:
235:
236:  Private Sub ThrowException(ByVal message As String)
237:    Throw New InvalidCastException(message)
238:  End Sub
239:
240: End Structure

The listing is quite long for a book but is included in its entirety for completeness. A circle is defined by a center point and radius. I included methods to calculate the area and diameter and included the Implements IConvertible statement (line 5) to indicate that Circle implements, or realizes, the IConvertible interface.

All the methods in Listing 9.13 are self-explanatory. It is important to note that only conversions that made sense were actually implemented to perform a conversion. Conversions that don't make sense—like ToDateTime—simply throw an exception. For the Circle structure it only made sense to implement GetTypeCode, ToType, and ToString. You will find practical implementations for these three methods; every other IConvertible method throws an exception. It is up to the consumer to trap exceptions when attempting a type conversion.

GetTypeCode (lines 93 through 96) returns the enumerated TypeCode value that defines the common type system type. ToType (lines 220 through 233) returns the Type object for supported conversion types. The Circle structure can be converted to a string representation, so the Type object for String is returned (lines 225 and 226) or an exception is thrown (lines 228 to 230) based on the conversionType argument. Finally, ToString (lines 211 through 218) actually performs the conversion to suitable, comma-delimited string representation of a circle.

Implementing a Type Converter

TypeConverter is a class that supports both design time and runtime type conversion. This class is especially useful with classes that need to be represented at design time in places like the Properties window. For our example, assuming a control had a circle property, it would be impractical to render the circle in the small field in the Properties window. However, the center point and radius numbers could easily be displayed. To support this practical conversion behavior, Listing 9.14 demonstrates a type converter for our Circle structure.

Listing 9.14. Implementing a Custom Type Converter for the Circle Structure
1:  Imports System.ComponentModel
2:  Imports System.Text.RegularExpressions
3:  Imports System.Globalization
4:
5:  Public Class CircleConverter
6:    Inherits TypeConverter
7:
8:    Private Const Pattern As String = _
9:      "^d+,d+,((d+)|(d+.d+))$"
10:
11:   Public Overloads Overrides Function CanConvertFrom( _
12:     ByVal context As ITypeDescriptorContext, _
13:     ByVal sourceType As Type) As Boolean
14:
15:     Return sourceType Is GetType(Rectangle) Or _
16:       sourceType Is GetType(RectangleF) Or _
17:       sourceType Is GetType(String) Or _
18:       MyBase.CanConvertFrom(context, sourceType)
19:
20:   End Function
21:
22:   Private Function IsValidString( _
23:     ByVal Value As Object) As Boolean
24:
25:     Return Value.GetType Is GetType(String) _
26:       AndAlso Regex.IsMatch(CType(Value, String), _
27:         Pattern)
28:
29:   End Function
30:
31:   Public Overloads Overrides Function ConvertFrom( _
32:     ByVal context As ITypeDescriptorContext, _
33:     ByVal info As CultureInfo, _
34:     ByVal Value As Object) As Object
35:
36:     If (CanConvertFrom(Value.GetType)) Then
37:
38:       If (IsValidString(Value)) Then
39:
40:         Dim s() As String = _
41:           CType(Value, String).Split(New Char() {","})
42:
43:         Return New Circle(Convert.ToInt32(s(0)), _
44:           Convert.ToInt32(s(1)), Convert.ToSingle(s(1)))
45:
46:       ElseIf (Value.GetType Is GetType(Rectangle)) Then
47:
48:         Dim R As Rectangle = CType(Value, Rectangle)
49:         Return New Circle(R.X + R.Width  2, _
50:           R.Y + R.Height  2, R.Width  2 + R.Height  2)
51:
52:       ElseIf (Value.GetType Is GetType(RectangleF)) Then
53:
54:         Dim R As RectangleF = CType(Value, RectangleF)
55:         Return New Circle(R.X + R.Width  2, _
56:           R.Y + R.Height  2, R.Width  2 + R.Height  2)
57:       Else
58:         Return MyBase.ConvertFrom(context, info, Value)
59:       End If
60:
61:     End If
62:
63:     Return MyBase.ConvertFrom(context, info, Value)
64:   End Function
65:
66:
67:   Public Overloads Overrides Function CanConvertTo( _
68:     ByVal context As ITypeDescriptorContext, _
69:     ByVal destinationType As Type) As Boolean
70:
71:     Return destinationType Is GetType(Rectangle) Or _
72:       destinationType Is GetType(RectangleF) Or _
73:       destinationType Is GetType(String) Or _
74:       MyBase.CanConvertTo(context, destinationType)
75:
76:   End Function
77:
78:   Public Overridable Overloads Function ConvertTo( _
79:     ByVal context As ITypeDescriptorContext, _
80:     ByVal culture As CultureInfo, _
81:     ByVal value As Object, _
82:     ByVal destinationType As Type _
83:     ) As Object
84:
85:     If (CanConvertTo(destinationType)) Then
86:       If (destinationType Is GetType(Rectangle)) Then
87:         Return CType(value, Circle).ToBoundingRectangle
88:       ElseIf (destinationType Is GetType(RectangleF)) Then
89:         Return CType(value, Circle).ToBoundingRectangleF
90:       ElseIf (destinationType Is GetType(String)) Then
91:         Return CType(value, Circle).ToString()
92:       End If
93:     Else
94:       Return MyBase.ConvertTo(context, culture, _
95:         value, destinationType)
96:     End If
97:
98:   End Function
99:
100: End Class

To implement the TypeConverter class we need to inherit from System.ComponentModel.TypeConverter and override the behavior of four inherited methods: CanConvertFrom, ConvertFrom, CanConvertTo, and ConvertTo. The names of these methods imply the forward and backward nature of a type converter. CanConvertFrom answers the question, “Which types can I convert to my type?” ConvertFrom performs the conversion from some other type to my type. CanConvertTo answers the question, “Which types can my type convert to?” ConvertTo actually performs the conversion to the request type.

As you might imagine, this involves a significant amount of type checking. We must examine the type to convert to or from and answer the question of suitability. For example, lines 11 through 20 in Listing 9.14 indicate that CircleConverter can create a Circle (that is, convert from any of the test types to a Circle) from a Rectangle, RectangleF, String, or any type the base class can convert from. The Rectangle integer can be used as a bounding rectangle, and the approximate dimensions of a circle can be derived from a bounding rectangle. The RectangleF structure is a rectangle with floating-point corners and can be used to approximate a circle in the same way that a Rectangle integer can be used. Finally, a String type that contains three numbers representing the center and radius could be used to derive a circle. The ITypeDescriptorContext interface can be used to determine the context (think “containing thing”) in which the conversion is being requested. You can include a condition in which a conversion is not supported for a particular context.

The ConvertFrom method (lines 31 through 64) is probably the most challenging method to implement. We have to write code that will convert from any particular type that we support to values sufficient to create an instance of our type. Because we support rectangles and strings, we need to write code that converts a rectangle or string to arguments suitable for a circle. For example, lines 36 through 44 determine whether the Value argument is an instance of a type we want to convert from. Line 38 checks to see whether Value is a string in the correct format. We defined the format, so we know it and can test for it in advance, using a regular expression to test the format and the String.Split method to actually subdivide the string. There are two points of interest here. First, we could have performed both the pattern check and the split with a regular expression. Second, we might want to add some error code rather than assuming there are three strings in the array and that those strings can be converted to the indicated type (lines 43 and 44). The two ElseIf conditions convert Rectangle types to Circle types, and the final Else condition invokes the base class behavior.

CanConvertTo and ConvertTo perform the analogous inverse conversion operations and are a bit simpler in our case. For example, converting from a Circle type to a Rectangle type is relatively easy because this behavior is supported in the Circle structure itself.

NOTE

Before you create a new converter, check the .NET Framework to determine whether a converter already exists. Check the TypeConverter Hierarchy help topic for a comprehensive list of converters. There are about three dozen listed in the Visual Studio .NET help documentation.


Associating a Type Converter with a Convertible Type

Next we need to tell the Circle structure and everybody else about the CircleConverter class. As you might have guessed, we use attributes to form the association between TypeConverter and Circle.

Use the TypeConverterAttribute to associate a custom type converter with the class that supports type conversion. Pass the Type object or the fully qualified name of the custom type converter to the TypeConverterAttribute. Line 3 of Listing 9.13 shows the TypeConverterAttribute applied with the Type object (returned by GetType) of the custom type converter, CircleConverter.

Using the IConvertible Behavior

You can download the ConversionDemo.sln program to test the implementation of IConvertible and CircleConverter. To invoke IConvertible methods, call System.Convert, passing an instance of a Circle object to the Convert class's shared methods. Listing 9.15 shows three event handlers that indirectly invoke methods in the Circle structure and attempt type conversions using the IConvertible interface.

Listing 9.15. Performing Runtime Type Conversion
Private Sub Button1_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles Button1.Click
  TextBox1.Text = Convert.ToString(Circle)
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles Button2.Click
  TextBox2.Text = Convert.GetTypeCode(Circle).ToString()
End Sub

Private Sub Button3_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles Button3.Click
  TextBox3.Text = Convert.ToDecimal(Circle)
End Sub

Each conversion is a single line of code that invokes a shared member of the System.Convert class. Double-checking with Rotor we can verify (assuming you can read C# code) that the shared conversion methods attempt to convert the target object to an IConvertible instance and invoke the implementation of the like method on the object itself. For example, our Circle object will be typecast to an IConvertible instance, and Convert.ToString will invoke the equivalent of CType(Circle, IConvertible).ToBoolean(Nothing), where Nothing is a null IFormatProvider. The shared Convert methods are overloaded. If you pass an IFormatProvider to ToBoolean, the provider will be passed on to the interface method instead of the literal Nothing. (The .NET Framework is implemented in C#; thus null is actually passed and the methods are declared static. Null and static are equivalent to VB's Nothing and Shared, respectively.)

IFormatProviders are objects that provide formatting instructions for conversions. Existing IFormatProviders include CultureInfo, DateTimeFormatInfo, and NumberFormatInfo. You can create a custom IFormatProvider for your types too. For example, we could implement CircleFormatInfo to describe relevant string representations of a circle. (Try this as an exercise.)

Using the Type Conversion Behavior

Type conversion can occur at design time or runtime. At design time it may be invoked by the IDE when an object needs to be displayed in the Properties window, for example, and we can perform explicit type conversion at runtime. Listing 9.16 demonstrates how to use the TypeDescriptor class to request the type converter for an object; subsequently, that type converter is used to perform the testing and conversion.

Listing 9.16. Performing Type Conversion Programmatically with a Type Descriptor
1:  Imports System.ComponentModel
2:
3:  Public Class Form1
4:      Inherits System.Windows.Forms.Form
5:
6:  [ Windows Form Designer generated code ]
7:
8:    Private Sub Button1_Click(ByVal sender As System.Object, _
9:      ByVal e As System.EventArgs) Handles Button1.Click
10:     TextBox1.Text = Convert.ToString(Circle)
11:   End Sub
12:
13:   Private Circle As Circle
14:
15:   Private Sub Form1_Paint(ByVal sender As Object, _
16:     ByVal e As System.Windows.Forms.PaintEventArgs) _
17:     Handles MyBase.Paint
18:
19:     e.Graphics.DrawEllipse(Pens.Red, _
20:       Circle.ToBoundingRectangle)
21:   End Sub
22:
23:   Private Sub Button2_Click(ByVal sender As System.Object, _
24:     ByVal e As System.EventArgs) Handles Button2.Click
25:     TextBox2.Text = Convert.GetTypeCode(Circle).ToString()
26:   End Sub
27:
28:   Private Sub Button3_Click(ByVal sender As System.Object, _
29:     ByVal e As System.EventArgs) Handles Button3.Click
30:     TextBox3.Text = Convert.ToDecimal(Circle)
31:   End Sub
32:
33:   Private Sub Form1_Load(ByVal sender As System.Object, _
34:     ByVal e As System.EventArgs) Handles MyBase.Load
35:
36:     Circle = New Circle(New Point(Me.Width  2, _
37:       Me.Height  2), Height  3)
38:   End Sub
39:
40:   Private Sub MenuItem4_Click(ByVal sender As System.Object, _
41:     ByVal e As System.EventArgs) Handles MenuItem4.Click
42:
43:     Dim About As String = _
44:       "IConvertible and TypeConverter Demo" & vbCrLf & _
45:       "Copyright " & Chr(169) & " 2002. All Rights Reserved." & _
46:       vbCrLf & _
47:       "By Paul Kimmel. [email protected]"
48:
49:     MessageBox.Show(About, "About", MessageBoxButtons.OK, _
50:        MessageBoxIcon.Information)
51:   End Sub
52:
53:   Private Sub MenuItem2_Click(ByVal sender As System.Object, _
54:     ByVal e As System.EventArgs) Handles MenuItem2.Click
55:
56:     Close()
57:
58:   End Sub
59:
60:   Private Sub MenuItem6_Click(ByVal sender As System.Object, _
61:     ByVal e As System.EventArgs) Handles MenuItem6.Click
62:
63:     Dim R = New Rectangle(100, 200, 75, 75)
64:     If (TypeDescriptor.GetConverter(GetType(Circle)). _
65:       CanConvertFrom(GetType(Rectangle))) Then
66:
67:       Circle = TypeDescriptor.GetConverter(GetType(Circle)). _
68:         ConvertFrom(R)
69:     End If
70:     Invalidate()
71:   End Sub
72:
73:   Private Sub MenuItem8_Click(ByVal sender As System.Object, _
74:     ByVal e As System.EventArgs) Handles MenuItem8.Click
75:
76:     Dim R = New RectangleF(150.5, 190.8, 33.7, 75.1)
77:     If (TypeDescriptor.GetConverter(GetType(Circle)). _
78:       CanConvertFrom(GetType(RectangleF))) Then
79:
80:       Circle = TypeDescriptor.GetConverter(GetType(Circle)). _
81:         ConvertFrom(R)
82:     End If
83:     Invalidate()
84:   End Sub
85:
86:   Private Sub MenuItem7_Click(ByVal sender As System.Object, _
87:     ByVal e As System.EventArgs) Handles MenuItem7.Click
88:
89:     Dim C As String = "200,200,160"
90:     If (TypeDescriptor.GetConverter(GetType(Circle)). _
91:       CanConvertFrom(GetType(String))) Then
92:
93:       Circle = TypeDescriptor.GetConverter(GetType(Circle)). _
94:         ConvertFrom(C)
95:     End If
96:     Invalidate()
97:
98:   End Sub
99: End Class

Listing 9.16 contains the code for the main form in ConversionDemo.sln. (You can see the repeated code from Listing 9.15, too.) Three menu items are similar in nature in lines 60 through 98. The code declares some type—for example, a string in line 89—that is an alternate representation of a circle. Following the representative type we use the TypeDescriptor.GetConverter shared method and Reflection implicitly to get the type converter for a type (lines 93 and 94) and invoke one of the test operations. In lines 90 and 91 we get the type converter for the Circle structure and ask whether a circle can be created from a given string. If the test succeeds, the related conversion method is invoked, again using the type converter. Lines 93 and 94 get the CircleConverter and convert the string C to an instance of the private field Circle.

Finally, the Invalidate method causes the form to be repainted, and the paint event handler (lines 15 through 21) draws the circle on the test form.

TIP

If you use the Option Strict On directive, you will need to perform explicit type casts using CType to convert the generic return type of ConvertFrom and ConvertTo to the converted type. (No explicit type casting is shown in Listings 9.15 and 9.16.)


Use almost identical code to determine whether a circle can be converted to an alternate type, invoking the CanConvertTo and ConvertTo methods and retrieving the converted type from the ConvertTo method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.20.101