Chapter 13. Interfaces

Most people have a car. Cars have an engine, four wheels, several gears, and other instrumentation. A lot of different companies produce cars, and each company makes several models. All companies have to build car models adhering to some particular specifications established by the law, and such specifications provide information on what minimal components will compose cars, including a list of those components that are therefore common to every car. In .NET development we can compare interfaces to the previously described law specifications. Interfaces provide a list of members that an object must implement to accomplish particular tasks in a standardized way. They are also known as contracts because they rule how an object must behave to reach some objectives. You saw an example in Chapter 8, “Managing an Object’s Lifetime,” for the IDisposable interface that must be implemented if an object wants to provide the capability to release resources that are not only in-memory objects, according to a standardized way. This means that the .NET Framework knows that the Dispose method from IDisposable is required to free up resources. In this chapter you first learn how to define and implement custom interfaces; then you get an overview of the most common interfaces in .NET Framework. You also learn why interfaces are important—because you will surely wonder why you should use them.

Defining Interfaces

An interface is a reference type defined within an Interface..End Interface block. Interfaces define only signatures for members that classes will then expose and are a set of the members’ definitions. Imagine you want to create an interface that defines members for working with documents. This is accomplished with the following code:

Public Interface IDocument

    Property Content As String
    Sub Load(ByVal fileName As String)
    Sub Save(ByVal fileName As String)

End Interface

The interface is marked as Public because the default scope for interfaces is Friend. Assigning public visibility ensures that external assemblies use the interface (which is a common scenario).


Interfaces Scope

Interfaces can be declared Private, Protected, or Protected Friend only if they are defined within a type such as a class.


As a convention, interface identifiers begin with a capital I. This is not mandatory (except when creating interfaces that are compliant with the Common Language Specification), but I strongly recommend you follow the convention. The most important consideration is that the interface definition contains only members’ definitions with no body. For both the Load and Save methods’ definitions, there is only a signature but not the method body and implementation, which are left to classes that implement the interface. Members defined within interfaces cannot be marked with one of the scope qualifiers, such as Public, Friend, and so on. By default, members defined by interfaces are Public. Finally, being reference types, interfaces need to be treated as such. See Chapter 4, “Data Types and Expressions,” for further information on reference types.


Nested Classes

Interfaces can define classes. A class defined within an interface is a typical Class..End Class block, as you would normally define one. This is an uncommon scenario and can be useful when you want to avoid naming conflicts with other classes, but you have to know that it is possible.


Implementing and Accessing Interfaces

Implementing interfaces means telling a class that it needs to expose all members defined within the interface. You do this by using the Implements keyword followed by the name of the interface. IntelliSense will offer a list of available interfaces, as demonstrated in Figure 13.1. The following code snippet shows how to implement the IDocument interface within a Document class:

Image

Figure 13.1. Choosing from available interfaces.

Public Class Document
    Implements IDocument

    Public Property Content As String Implements IDocument.Content

    Public Sub Load(ByVal fileName As String) Implements IDocument.Load
    End Sub

    Public Sub Save(ByVal fileName As String) Implements IDocument.Save
    End Sub

End Class

You’ll notice that, when pressing Enter, the Visual Studio IDE automatically generates members’ templates for you, as represented in Figure 13.2.

Image

Figure 13.2. Visual Studio generates a skeleton for members based on the interface.

This is useful because it saves you from having to waste your time writing members’ signatures. You’ll also notice that when a member is defined within a class because of the interface implementation, the Implements keyword is also added at the end of the member followed by the related element in the interface.


Multiple Implementations

Different from inheritance, classes and structures can implement more than one interface. You see an example later in the chapter when discussing IEnumerable and IEnumerator.


The Document class is basic and is for demo purposes only. To complete the implementation example, we can write code to populate methods for performing operations established in the interface, as shown in Listing 13.1.

Listing 13.1. Implementing Interfaces


Public Class Document
    Implements IDocument

    Public Property Content As String Implements IDocument.Content

    'Gets the content of a text document
    Public Sub Load(ByVal fileName As String) Implements IDocument.Load
        Try
            Content = My.Computer.FileSystem.ReadAllText(fileName)
        Catch ex As Exception
            Throw
        End Try
    End Sub

    'Saves a text document to file
    Public Sub Save(ByVal fileName As String) Implements IDocument.Save
        Try
            My.Computer.FileSystem.WriteAllText(fileName,
                                                Content, False)
        Catch ex As Exception
            Throw
        End Try
    End Sub

End Class


When you implement interfaces, you need to populate members’ templates with your own code. This can ensure that your object is respecting the contract established by the interface. When a class implements an interface, it also needs to access members it defines. You have two alternatives for this purpose. The first one is simple and intuitive and consists of creating an instance of the class that implements the interface. Continuing with the example of the Document class shown in Listing 13.1, the following code shows how you can accomplish this:

Dim myDocument As New Document
myDocument.Load("SomeDocument.txt")

Console.WriteLine(myDocument.Content)

The code invokes instance members of the class with no differences for normal classes’ implementations. The second alternative is declaring an interface variable. You declare a variable whose type is the interface; the variable receives the result of an explicit conversion from the class that implements the interface to the interface itself. More than words, code can provide a good explanation:

Dim myDocument As IDocument = CType(New Document, IDocument)
myDocument.Load("SomeDocument.txt")

Console.WriteLine(myDocument.Content)

The result is the same. You often find code that makes use of interface variables, so spend a little time becoming familiar with this approach. After this discussion, you will probably wonder why you need to define and implement interfaces because you just need to write code as you would do without them. The answer is polymorphism.

Passing Interfaces as Method Arguments

One of the most powerful features when working with interfaces is that methods can receive interfaces as parameters. This means that you can pass in any object as these parameters so long as it implements the given interface. The following example shows a method that accepts an argument of type IList, meaning that any object implementing the IList interface can be accepted:

'Interfaces as parameters
Public Class WorkWithLists
    Public Function Elaborate(ByVal items As IList) As Integer
        'Just for demo, returns 0 if the list contains something
        If items.Count > 0 Then
            Return 0
        Else
            'if not, adds a new object to the list
            Dim item As New Object
            items.Add(item)
            Return -1
        End If
    End Function

End Class

This is with no doubt one of the most important features in programming by contracts with interfaces.

Interfaces and Polymorphism

Chapter 12, “Inheritance,” discusses polymorphism, which offers a common infrastructure to different types of objects. In the discussion, interfaces find their natural habitat. They provide a common set of members that classes need to implement if they need to perform a particular series of tasks. A typical example is the IDisposable interface that you met in Chapter 8. All classes that need to provide a mechanism for releasing resources implement that interface, which exposes a set of common members. Another example is the ICloneable interface that defines a Clone method that classes can implement to provide the capability to copy a class instance. You can easily understand that interfaces are generic; they are not specific to any class but are instead as generic as possible so that the widest variety of classes can implement them. To provide a code example, let’s reexamine the IDocument interface proposed in the previous section. This interface was implemented by a Document class. But the same interface can be implemented in other kinds of classes. For example, we can define an Invoice class that can implement the same IDocument interface because it exposes a common set of members that can be easily used within the Invoice class. Then the new class can provide new members specific to its particular needs and behavior. The following code demonstrates this:

Public Class Invoice
    Implements IDocument

    Public Property Content As String Implements IDocument.Content

    Public Sub Load(ByVal fileName As String) Implements IDocument.Load

    End Sub

    Public Sub Save(ByVal fileName As String) Implements IDocument.Save

    End Sub

    Public Property InvoiceNumber As Integer

    Public Function CalculateDiscount(ByVal price As Decimal,
                                      ByVal percent As Single) As Decimal

    End Function

End Class

As you can see, the IDocument interface can serve the Invoice class with its members; then the class defines new members (InvoiceNumber and CalculateDiscount) strictly related to its behavior. By the way, the IDocument interface provides polymorphic code that can be used in different situations and objects with a common infrastructure.

Interfaces Inheritance

Two situations are related to both inheritance and interfaces. The first scenario occurs when you create a class that derives from another one that implements an interface. In such a scenario, the derived class also inherits members implemented through an interface and does not need to implement the interface again. Moreover, if the base class contains members that are marked as overridable and implemented via an interface, the derived class can override such members if not private. The second scenario is a pure interface inheritance, in which an interface can inherit from another one. Continuing the previous examples, we can consider creating an IInvoice interface that inherits from IDocument and provides some more specific members to represent an invoice. The following code demonstrates this:

Public Interface IInvoice
    Inherits IDocument

    'New members
    Property InvoiceNumber As Integer
    Function CalculateDiscount(ByVal price As Decimal,
                               ByVal percent As Single) As Decimal

End Interface

As you can see, the Inherits keyword is used also for interface inheritance. In this example, the new interface inherits all members’ definitions from the IDocument interface and adds two new members—the InvoiceNumber property and the CalculateDiscount method. After this, you could rewrite the Invoice class as follows:

Public Class Invoice
    Implements IInvoice

    Public Property Content As String Implements IInvoice.Content

    Public Sub Load(ByVal fileName As String) Implements IInvoice.Load

    End Sub

    Public Sub Save(ByVal fileName As String) Implements IInvoice.Save

    End Sub

    Public Property InvoiceNumber As Integer Implements IInvoice.InvoiceNumber

    Public Function CalculateDiscount(ByVal price As Decimal,
                                      ByVal percent As Single) As Decimal Implements
IInvoice.CalculateDiscount

    End Function
End Class

It is worth mentioning that, due to inheritance, members exposed by the base interface can be implemented using the name of the base interface, instead of the derived one. For example, in the previous code snippet the Content property is implemented like this:

Public Property Content As String Implements IInvoice.Content

Because this property is exposed by the IDocument interface and inherited by IInvoice, the compiler also accepts the following implementation:

Public Property Content As String Implements IDocument.Content

Defining CLS-Compliant Interfaces

The Common Language Specification (CLS) also provides rules for interfaces. The first rule is that if you mark an interface as CLS-compliant, you cannot use CLS-noncompliant types within signatures. The following interface is not correct because it is marked as CLSCompliant but uses a noncompliant type:

'Incorrect: UInteger is not CLS compliant
<CLSCompliant(True)> Public Interface ITest

    Property Counter As UInteger

End Interface

The second rule is that a CLS-compliant interface cannot define shared members. The last rule is that all members must be explicitly marked with the CLSCompliant attribute. The following is an example of a CLS-compliant interface:

<CLSCompliant(True)> Public Interface IClsCompliant

    <CLSCompliant(True)> Property Counter As Integer
    <CLSCompliant(True)> Function DoSomething() As Boolean

End Interface


Naming Conventions

It is an implicit rule that identifiers for all CLS-compliant interfaces must begin with a capital I. IDocument is a correct identifier, whereas MyDocumentInterface is not. Identifiers cannot contain the underscore character (_) and are written according to the Pascal-casing conventions.


Most Common .NET Interfaces

Because of their importance in polymorphism, the .NET Framework defines a large quantity of interfaces implemented by most types within the Framework. You need to understand the most common built-in interfaces because they provide great flexibility in your code, and in several situations you need to implement such interfaces in your objects that need to perform particular tasks. Table 13.1 summarizes the most common .NET interfaces.

Table 13.1. Most Common Interfaces

Image

The ICloneable interface is discussed in Chapter 4, and the IDisposable interface is discussed in Chapter 8. This chapter therefore does not revisit these interfaces. Instead, you’ll learn how you can implement the other ones in your code.

The IEnumerable Interface

You implement the IEnumerable interface each time you want your class to support For..Each loops. Each time you iterate an object (typically a collection) using For Each, it is because that object implements IEnumerable. The .NET Framework offers lots of collections (including generic ones) and enables you to create custom collections inheriting from built-in ones; therefore, implementing IEnumerable will probably be spared for you. It’s important to understand how the interface works, especially for its intensive usage when working with LINQ. IEnumerable provides one method, named GetEnumerator, which generally is implemented as follows:

Public Function GetEnumerator() As System.Collections.IEnumerator _
    Implements System.Collections.IEnumerable.GetEnumerator
    Return CType(Me, IEnumerator)

End Function

As you can see, the method returns the result of the conversion of the class instance to an IEnumerator object; this means that IEnumerable must be implemented together with another interface. It’s named IEnumerator and offers methods and properties for moving between items in a collection and for providing information on the current item. To provide an example, imagine you have a class named Contacts that acts as a repository of items of type Contact and that implements IEnumerable to provide iteration capabilities. Listing 13.2 shows how this is accomplished in code, including a sample loop performed invoking For..Each.

Listing 13.2. Implementing IEnumerable and IEnumerator


Public Class Contacts
    Implements IEnumerable, IEnumerator

    Public Function GetEnumerator() As System.Collections.IEnumerator _
        Implements System.Collections.IEnumerable.GetEnumerator
        Return CType(Me, IEnumerator)
    End Function

    Private position As Integer = -1

    Public ReadOnly Property Current As Object _
        Implements System.Collections.IEnumerator.Current
        Get
            Return Items(position)
        End Get
    End Property

    Public Function MoveNext() As Boolean _
        Implements System.Collections.IEnumerator.MoveNext
        position += 1
        Return (position < Items.Length)
    End Function

    Public Sub Reset() Implements System.Collections.IEnumerator.Reset
        position = -1
    End Sub

    Private Items() As Contact = New Contact() {New Contact With _
                                                {.FirstName = "Alessandro",
                                                .LastName = "Del Sole",
                                                .Email = "alessandro.delsole" & _
                                                         "@visual-basic.it",
                                                .PhoneNumber = "000-0000-00"},
                                                New Contact With _
                                                {.FirstName = "Robert",
                                                .LastName = "Green",
                                                .Email = "[email protected]",
                                                .PhoneNumber = "000-0000-00"} _
                                                }

End Class

Public Class Contact

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Email As String
    Public Property PhoneNumber As String

End Class
Module Module1

    Sub Main()

        Dim c As New Contacts
        'Returns "Del Sole", "Green"
        For Each Cont As Contact In c
            Console.WriteLine(Cont.LastName)
        Next

        Console.ReadLine()
    End Sub
End Module


The Contacts class stores an array of Contact objects and provides a private field, position, which is used for returning information. The Current property returns the item in the array corresponding to the current position, whereas the MoveNext method increments the position variable and returns True if the position number is still less than the upper bound in the array. In the end, Reset just restores the initial value for position. You also notice how, within the Sub Main in Module1, a simple For..Each loop is given for demonstration purposes. If you run the code, you see that it correctly returns last names for both actual contacts within the Contacts class.


IEnumerable(Of T)

As for other interfaces, a generic version of IEnumerable supports specific types. Because Chapter 15, “Delegates and Events,” discusses generics, this chapter shows the non-generic version that works the same, except that it is related to Object.


The IComparable Interface

You implement the IComparable interface when you want to offer custom comparison instrumentation to your objects. IComparable requires you to implement a CompareTo method that returns an Integer value that is less than zero if the instance is less than the compared object, is zero if the instance equals the compared object, and is greater than zero if the instance is greater than the compared object. For example, imagine you want to provide a comparison to the Person class based on the length of the LastName property. Listing 13.3 shows how you can accomplish this.

Listing 13.3. Implementing the IComparable Interface


Public Class Person
    Implements IComparable

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Email As String


    Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
        If Not TypeOf (obj) Is Person Then
            Throw New ArgumentException
        Else

            Dim tempPerson As Person = DirectCast(obj, Person)

            If Me.LastName.Length < tempPerson.LastName.Length Then
                Return -1
            ElseIf Me.LastName.Length = tempPerson.LastName.Length Then
                Return 0
            Else
                Return 1
            End If

        End If
    End Function
End Class
Module Module1

    Sub Main()
        Dim p1 As New Person With {.LastName = "Del Sole",
                                   .FirstName = "Alessandro"}
        Dim p2 As New Person With {.LastName = "AnotherLastName",
                                   .FirstName = "AnotherFirstName"}

        Dim c As New ComparableHelper(p1)
        Console.WriteLine(c.CompareTo(p2))
        Console.ReadLine()
    End Sub

End Module


You might notice that a first check is performed on the object type, which must be Person. If not, the code throws an ArgumentException (meaning that the argument is not valid). The comparison is accomplished in a simple way using unary operators. Next, to perform the comparison, you just need to create an instance of the Person class and then invoke its CompareTo method, passing the Person you want to compare to the current instance.


IComparer Interface

If you want to provide custom sorting for arrays, you need to implement the IComparer interface. The following article in the Microsoft Knowledge Base provides a good example that is extensible to Visual Basic 2012: http://support.microsoft.com/kb/321292/en-us


Utilizing the Generic IComparable(Of T)

Although Chapter 15 discusses generics, this is a good point for showing something interesting about them. Many interfaces within the .NET Framework have a generic counterpart. For example, there is an IEnumerable(Of T) or IComparable(Of T), in which T is a specific .NET type instead of Object, which would require conversions and, therefore, performance overhead. We could rewrite the Person class shown in Listing 13.3 using the IComparable(Of T) interface to provide support for Person objects. This is accomplished with the following code:

Public Class Person
    Implements IComparable(Of Person)

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Email As String

    Public Function CompareTo(ByVal other As Person) As Integer _
           Implements System.IComparable(Of Person).CompareTo
        If Me.LastName.Length < other.LastName.Length Then
            Return -1
        ElseIf Me.LastName.Length = other.LastName.Length Then
            Return 0
        Else
            Return 1
        End If
    End Function

End Class

You will soon notice how DirectCast conversions disappear and how the CompareTo method receives an argument of type Person instead of Object. This means less code and more precision. In Chapter 15 and Chapter 16, “Working with Collections and Iterators,” you gain detailed information about generics and generic collections.

The IConvertible Interface

Objects implementing the IConvertible interface expose a series of ToXXX methods exactly as the Convert class does so that such objects can easily be converted into another type. This example uses a structure instead of a class, for the sake of simplicity. Listing 13.4 shows how the IConvertible interface can be implemented.

Listing 13.4. Implementing the IConvertible Interface


Public Structure ThreePoint
    Implements IConvertible

    Public Function GetTypeCode() As System.TypeCode Implements _
        System.IConvertible.GetTypeCode

        Return TypeCode.Object
    End Function

    'Just a custom return value
    Public Function ToBoolean(ByVal provider As System.IFormatProvider) _
        As Boolean Implements System.IConvertible.ToBoolean
        Return X > Y
    End Function

    Public Function ToByte(ByVal provider As System.IFormatProvider) _
        As Byte Implements System.IConvertible.ToByte
        Return Convert.ToByte(SumPoints)
    End Function

    Public Function ToChar(ByVal provider As System.IFormatProvider) _
        As Char Implements System.IConvertible.ToChar
        Return Convert.ToChar(SumPoints)
    End Function

    Public Function ToDateTime(ByVal provider As System.IFormatProvider) _
        As Date Implements System.IConvertible.ToDateTime
        Return Convert.ToDateTime(SumPoints)
    End Function

    Public Function ToDecimal(ByVal provider As System.IFormatProvider) _
        As Decimal Implements System.IConvertible.ToDecimal
        Return Convert.ToDecimal(SumPoints)
    End Function

    Public Function ToDouble(ByVal provider As System.IFormatProvider) _
        As Double Implements System.IConvertible.ToDouble
        Return Convert.ToDouble(SumPoints)
    End Function

    Public Function ToInt16(ByVal provider As System.IFormatProvider) _
        As Short Implements System.IConvertible.ToInt16
        Return Convert.ToInt16(SumPoints)
    End Function

    Public Function ToInt32(ByVal provider As System.IFormatProvider) _
        As Integer Implements System.IConvertible.ToInt32
        Return SumPoints()
    End Function

    Public Function ToInt64(ByVal provider As System.IFormatProvider) _
        As Long Implements System.IConvertible.ToInt64
        Return Convert.ToInt64(SumPoints)
    End Function

    Public Function ToSByte(ByVal provider As System.IFormatProvider) _
        As SByte Implements System.IConvertible.ToSByte
        Return Convert.ToSByte(SumPoints)
    End Function

    Public Function ToSingle(ByVal provider As System.IFormatProvider) _
        As Single Implements System.IConvertible.ToSingle
        Return Convert.ToSingle(SumPoints)
    End Function

    'Required "Overloads"
    Public Overloads Function ToString(ByVal provider As System.IFormatProvider) _
        As String Implements System.IConvertible.ToString
        Return String.Format("{0}, {1}, {2}", Me.X, Me.Y, Me.Z)
    End Function

    Public Function ToType(ByVal conversionType As System.Type,
                           ByVal provider As System.IFormatProvider) _
                           As Object Implements System.IConvertible.ToType
        Return Convert.ChangeType(SumPoints, conversionType)
    End Function

    Public Function ToUInt16(ByVal provider As System.IFormatProvider) _
        As UShort Implements System.IConvertible.ToUInt16

        Return Convert.ToUInt16(SumPoints)
    End Function

    Public Function ToUInt32(ByVal provider As System.IFormatProvider) _
        As UInteger Implements System.IConvertible.ToUInt32
        Return Convert.ToUInt32(SumPoints)
    End Function

    Public Function ToUInt64(ByVal provider As System.IFormatProvider) _
        As ULong Implements System.IConvertible.ToUInt64
        Return Convert.ToUInt64(SumPoints)
    End Function

    Public Property X As Integer
    Public Property Y As Integer
    Public Property Z As Integer

    Public Sub New(ByVal valueX As Integer,
                   ByVal valueY As Integer,
                   ByVal valueZ As Integer)
        Me.X = valueX
        Me.Y = valueY
        Me.Z = valueZ
    End Sub

    Public Function SumPoints() As Integer
        Return (Me.X + Me.Y + Me.Z)
    End Function
End Structure


The ThreePoint structure is simple; it exposes three integer properties (x, y, and z) whose sums are returned via a SumPoints method. The goal of the IConvertible implementation is therefore to enable returning the result of the method converted into different types. Each conversion method invokes the corresponding one of the Convert class, with some exceptions. The first one is the ToBoolean method, which returns a customized result depending on the value of x and y, but this is just for demonstration purposes. (You can invoke the Convert.ToBoolean as well.) The second exception is the ToString method. When you implement an interface that provides a method already existing in the class (even because of inheritance), Visual Studio renames the interface’s method by adding a 1 in the end. For example, every class exposes ToString because it is inherited from System.Object. Thus, the ToString version provided by the interface is automatically renamed to ToString1. But this is not elegant. A better technique is overloading because the method is marked with Overloads and named correctly.


Important Notice

IConvertible does not adhere to the CLS because it makes use of CLS-incompliant types, such as UInt16, UInt32, and so on. You should be aware of this if you plan to develop objects that need to be CLS-compliant.


The IFormattable Interface

The IFormattable interface enables implementing a new overload of the ToString method to provide customized string formatting with deep control over the process, also defining custom qualifiers. Listing 13.5 shows how you can implement the IFormattable interface.

Listing 13.5. Implementing the IFormattable Interface


Imports System.Globalization

Public Class Person
    Implements IFormattable

    Public Property FirstName As String
    Public Property LastName As String
    Public Property Email As String

    Public Overloads Function ToString(ByVal format As String,
                              ByVal formatProvider As System.IFormatProvider) _
                              As String Implements System.IFormattable.ToString

        If String.IsNullOrEmpty(format) Then format = "G"
        If formatProvider Is Nothing Then formatProvider = _
                                          CultureInfo.CurrentCulture

        Select Case format
            'General specifier. Must be implemented
            Case Is = "G"
                Return String.Format("{0} {1}, {2}",
                       Me.FirstName, Me.LastName, Me.Email)
            Case Is = "F"
                Return FirstName
            Case Is = "L"
                Return LastName
            Case Is = "LF"
                Return String.Format("{0} {1}", Me.LastName, Me.FirstName)
            Case Else
                Throw New FormatException
        End Select
    End Function
End Class

Module Module1
    Sub Main()
        Dim p As New Person With {.FirstName = "Alessandro",
            .LastName = "Del Sole",
            .Email = "[email protected]"}

        Console.WriteLine("{0:G}", p)
        Console.WriteLine("{0:L}", p)
        Console.WriteLine("{0:F}", p)
        Console.WriteLine("{0:LF}", p)
        Console.ReadLine()
    End Sub
End Module


Listing 13.5 shows a particular implementation of the Person class, which exposes the FirstName, LastName, and Email properties. It implements the IFormattable interface that offers a new overload of the ToString method. This method receives two arguments; the first one, format, represents the qualifier. For example, in standard formatting, the letter c represents currency. Here you can specify your own qualifiers. Because of this, the first check is whether the format is null or empty. In such case, a G qualifier is assigned by default. G stands for General and is the only qualifier that must be implemented. When you provide G, you can create your own qualifiers. The next check is on formatProvider that represents the culture for string formatting. If it’s null, the code assigns the local system culture. The subsequent Select..End Select block takes into consideration some identifiers as custom qualifiers. L stands for LastName, F stands for FirstName, and LF stands for LastName + FirstName. You can change or extend this code by intercepting your custom identifiers within this block. Running the code shown in Listing 13.5 produces the following result:

Alessandro Del Sole, [email protected]
Del Sole
Alessandro

Del Sole Alessandro

You can easily compare the output result with the code and understand how custom formatting works.

Summary

This chapter focused on interfaces, another key topic in the object-oriented programming. Interfaces, which are defined within Interface..End Interface blocks, provide signatures of sets of members that an object must implement to accomplish specific tasks. You might remember the example of the IDisposable interface that must be implemented by objects that need to provide methods for freeing up resources. You saw how to create and implement custom interfaces in your code via the Implements keyword and then how to invoke objects that implement interfaces both by creating class instances and via interface variables. You found out why interfaces are important and why the .NET Framework makes an intensive usage of interfaces, talking about polymorphism and how interfaces can contribute to provide standardized infrastructures for multiple objects. It’s important to adhere to the Common Language Specification when defining interfaces, so you got information about this. Finally, code examples have been provided for the most common .NET built-in interfaces, to provide a deeper understanding of how things happen behind the scenes and to reuse those interfaces in your code.

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

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