Section 1 Interface description
An interface is used to define a protocol's contract. The class or structure that implements an interface must be strictly consistent with the definition of the interface. With this agreement, the limitations of programming languages can be aside (theoretical). An interface can be inherited from multiple base interfaces, while a class or structure can implement multiple interfaces. An interface can contain methods, properties, events, and indexers. The interface itself does not provide an implementation of the members it defines. An interface specifies only the class or members that the interface must provide to implement the interface.
An interface is like a template that defines the methods that an object must implement, and its purpose is to enable these methods to be referenced as interface instances. The interface cannot be instantiated. A class may implement multiple interfaces and the interfaces implemented through these are indexed. Interface variables can only index instances of classes that implement the interface. example:
interface IMyExample { string this[int index] { get ; set ; } event EventHandler Even ; void Find(int value) ; string Point { get ; set ; } } public delegate void EventHandler(object sender, Event e) ;
The interface in the example above contains an index this, an event Even, a method Find and a property Point.
Interfaces can support multiple inheritance. Just like in the following example, the interface "IComboBox" inherits from both "ITextBox" and "IListBox".
interface IControl { void Paint( ) ; } interface ITextBox: IControl { void SetText(string text) ; } interface IListBox: IControl { void SetItems(string[] items) ; } interface IComboBox: ITextBox, IListBox { }
Classes and structures can multiple instantiate interfaces. Just like in the following example, the class "EditBox" inherits the class "Control", and inherits from both "IDataBound" and "IControl".
interface IDataBound { void Bind(Binder b) ; } public class EditBox: Control, IControl, IDataBound { public void Paint( ) ; public void Bind(Binder b) {...} }
In the above code, the "Paint" method comes from the "IControl" interface; the "Bind" method comes from the "IDataBound" interface, and is implemented in the "EditBox" class as "public".
illustrate:
1. Interfaces in C# are defined independently of classes. This is opposite to the C++ model, where interfaces are actually abstract base classes.
2. Both interfaces and classes can inherit multiple interfaces.
3. A class can inherit a base class, but an interface cannot inherit a class at all. This model avoids the problem of multiple inheritance in C++, and implementations in different base classes in C++ may conflict. Therefore, complex mechanisms such as virtual inheritance and explicit scope are no longer needed. The simplified interface model of C# helps speed up application development.
4. An interface defines a reference type with only abstract members. What an interface in C# actually does is only the method flag, but there is no code executing at all. This implies that an interface cannot be instantiated, only an object derived from that interface can be instantiated.
5. Interfaces can define methods, properties and indexes. Therefore, compared with a class, the speciality of an interface is: when defining a class, it can be derived from multiple interfaces, and you can only derive from only one class.
Interfaces and components
The interface describes the services provided by the component to the outside. Interacting between components and components, between components and customers through interfaces. Therefore, once a component is released, it can only provide reasonable and consistent services through predefined interfaces. This stability between interface definitions enables customer application developers to construct rugged applications. A component can implement multiple component interfaces, and a specific component interface can also be implemented by multiple components.
Component interfaces must be self-described. This means that component interfaces should not rely on specific implementations, and separation of implementation and interface completely eliminates the coupling relationship between the interface user and the interface implementer, enhancing the degree of information encapsulation. At the same time, this also requires that component interfaces must use a language that is independent of component implementation. The current standard for describing component interfaces is IDL language.
Since interfaces are protocols between components, once the component's interface is released, component producers should keep the interface unchanged as much as possible. Any changes in the interface syntax or semantics may cause the connection between existing components and customers to be damaged.
Each component is autonomous and has its own unique functions and can only communicate with the outside world through an interface. When a component needs to provide new services, it can be achieved by adding new interfaces. It will not affect customers whose original interface already exists. New customers can re-select new interfaces to obtain services.
Component-based programming
The componentized programming method inherits and develops an object-oriented programming method. It applies object technology to system design and further abstracts the implementation process of object-oriented programming. We can use component-based programming methods as a method of constructing the architectural hierarchy of the system, and we can easily implement components using an object-oriented method.
Componented programming emphasizes true software reusability and high interoperability. It focuses on the generation and assembly of components, and these two aspects together form the core of componentized programming. The process of generating components is not just a demand for application systems, but the component market itself also promotes the development of components and promotes communication and cooperation among software manufacturers. The assembly of components allows software products to be quickly established using methods similar to building blocks, which not only shortens the development cycle of software products, but also improves the stability and reliability of the system.
The component programming method has the following characteristics:
1. Independence between programming language and development environment;
2. Transparency of component location;
3. Process transparency of components;
4. Expansion;
5. Reusability;
6. Have strong infrastructure;
7. System-level public services;
Due to its many advantages, C# language is very suitable for component programming. But this does not mean that C# is a component programming language, nor does it mean that C# provides a tool for component programming. We have pointed out many times that components should have programming language-independent features. Please remember this: Component model is a specification, and no matter what programming language is used to design components, it must be followed. For example, in the example of assembling computers, as long as the accessories specifications and interfaces provided by each manufacturer meet unified standards, these accessories can work together when combined, and the same is true for component programming. We just said that using C# language for component programming will bring us greater convenience.
After knowing what an interface is, the next step is how to define an interface. Please see the next section - Defining an interface.
Section 2 Defining the interface
Technically, an interface is a set of data structures containing functional methods. Through this set of data structures, the client code can call the functions of component objects.
The general form of defining an interface is:
[attributes] [modifiers] interface identifier [:base-list] {interface-body}[;]
illustrate:
1. Attributes (optional): additional defining information.
2. Modifiers (optional): The modifiers allowed are new and four access modifiers. They are: new, public, protected, internal, and private. The same modifier is not allowed to appear multiple times in an interface definition.
Modifiers can only appear in nested interfaces, indicating that they overwrite inherited members of the same name. The public, protected, internal, and private modifiers define access to the interface.
3. Indicators and events.
4. identifier: interface name.
5. base-list (optional): contains a list of one or more explicit base interfaces, separated by commas.
6. interface-body: Definition of interface members.
7. An interface can be a member of a namespace or class, and can contain the signatures of the following members: Methods, properties, indexers.
8. An interface can be inherited from one or more base interfaces.
The concept of interface is very similar in C# and Java. The keyword of an interface is interface, and an interface can extend one or more other interfaces. By convention, the name of the interface begins with the capital letter "I". The following code is an example of a C# interface, which is exactly the same as an interface in Java:
interface IShape { void Draw ( ) ; }
If you derive from two or more interfaces, the name list of the parent interface is separated by commas, as shown in the following code:
interface INewInterface: IParent1, IParent2 { }
However, unlike Java, interfaces in C# cannot contain fields (Field). Also note that in C#, all methods within the interface are public methods by default. In Java, method definitions can carry public modifiers (even if this is not necessary), but in C# it is illegal to explicitly specify public modifiers for methods of interfaces. For example, the following C# interface will generate a compilation error.
interface IShape { public void Draw( ) ; }
The following example defines an interface called IControl, which contains a member method Paint:
interface IControl { void Paint( ) ; }
In the following example, interface IInterface inherits from two base interfaces IBase1 and IBase2:
interface IInterface: IBase1, IBase2 { void Method1( ) ; void Method2( ) ; }
The interface can be implemented by a class. The identifier of the implemented interface appears in the base list of the class. For example:
class Class: Iface, Iface { // class member.// /roucheng/ }
When the base list of a class contains both a base class and an interface, the first thing that appears in the list is the base class. For example:
class ClassA: BaseClass, Iface1, Iface2 { // class member.}
The following code snippet defines the interface IFace, which has only one method:
interface IFace { void ShowMyFace( ) ; }
An object cannot be instantiated from this definition, but a class can be derived from it. Therefore, the class must implement the ShowMyFace abstract method:
class CFace:IFace { public void ShowMyFace( ) { (" implementation " ) ; } }
Base interface
An interface can be inherited from zero or more interfaces, those explicit base interfaces called this interface. When an interface has more explicit base interfaces than zero, the interface definition is followed by a list of base interface identifiers separated by a colon ":" and a comma "," ".
Interface base:
: Interface type list description:
1. The explicit base interface of an interface must be at least accessible as the interface itself. For example, it is wrong to specify a private or internal interface in the base interface of a public interface.
2. It is wrong to inherit an interface directly or indirectly from itself.
3. The base interfaces of interfaces are all explicit base interfaces and are their base interfaces. In other words, the set of base interfaces consists entirely of explicit base interfaces and their explicit base interfaces, etc. In the following example
interface IControl { void Paint( ) ; } interface ITextBox: IControl { void SetText(string text) ; } interface IListBox: IControl { void SetItems(string[] items) ; } interface IComboBox: ITextBox, IListBox { }
The base interfaces of IComboBox are IControl, ITextBox, and IlistBox.
4. An interface inherits all members of its base interface. In other words, the above interface IComboBox inherits members SetText and SetItems just like Paint.
5. A class or structure that implements an interface also implicitly implements the base interface of all interfaces.
Interface body
The interface body of an interface defines the members of the interface.
interface-body: { interface-member-declarationsopt }
Defining an interface is mainly to define interface members. Please see the next section - Defining an interface member.
Section 3 Defining interface members
An interface can contain one and multiple members, which can be methods, properties, index indicators, and events, but cannot be constants, domains, operators, constructors, or destructors, and cannot contain any static members. The interface definition creates a new definition space, and the interface member definition directly contains the interface member definition to introduce new members into the definition space.
illustrate:
1. The members of an interface are members inherited from the base interface and members defined by the interface itself.
2. Interface definition can define zero or more members. Members of an interface must be methods, properties, events, or indexers. An interface cannot contain constants, fields, operators, instance constructors, destructors, or types, nor can it contain any kind of static members.
3. Define an interface that contains one for each possible member: method, property, event, and indexer.
4. The default access method of interface members is public. Interface member definitions cannot contain any modifiers, such as abstract, public, protected, internal, private, virtual, override, or static modifiers cannot be added before the member definition.
5. The members of the interface cannot have the same name. Inherited members do not need to be defined anymore, but the interface can define members with the same name as the inherited members. At this time, we say that the interface members overwrite the inherited members, which will not cause an error, but the compiler will give a warning. The way to turn off the warning prompt is to add a new keyword before the member definition. However, if members in the parent interface are not overwritten, using the new keyword will cause the compiler to issue a warning.
6. The name of the method must be different from the names of all attributes and events defined in the same interface. Furthermore, the signature of the method must be different from the signature of all other methods defined in the same interface.
7. The name of the property or event must be different from the names of all other members defined in the same interface.
8. The signature of an indexer must be different from the signature of all other indexers defined in the same interface.
9. The attributes, return type, identifier, and formal-parameter-lis in the interface method declaration have the same meaning as those in the method declaration of a class. An interface method declaration does not allow specifying a method body, and the declaration usually ends with a semicolon.
10. The access character declared in the interface attribute corresponds to the access character declared in the class attribute, except that the access character body must usually use a semicolon. Therefore, whether the attribute is read-write, read-only or write-only, the access character is completely certain.
11. The attributes, types, and formal-parameter-list in the interface index declaration have the same meaning as those of the class index declaration.
In the following example, the interface IMyTest contains the index indicator, event E, method F, and attribute P:
interface IMyTest{ string this[int index] { get; set; } event EventHandler E ; void F(int value) ; string P { get; set; } } public delegate void EventHandler(object sender, EventArgs e) ;
In the following example, the interface IStringList contains the interfaces for each possible type member: a method, a property, an event, and an index.
public delegate void StringListEvent(IStringList sender); public interface IStringList { void Add(string s); int Count { get; } event StringListEvent Changed; string this[int index] { get; set; } }
The full name of the interface member
The full qualified name can also be used for interface members. This is how the full name of the interface is formed. Add small dots to the interface name "." and then to the member name, for example, for the following two interfaces:
interface IControl { void Paint( ) ; } interface ITextBox: IControl { void GetText(string text) ; }
The full name of Paint is, and the full name of GetText is ITextBox. GetText. Of course, the member names in the full name must be defined in the interface, such as using it. It is unreasonable.
If the interface is a member of the namespace, the full name must also contain the name of the namespace.
namespace System { public interface IDataTable { object Clone( ) ; } }
Then the full name of the Clone method is System.
After defining the interface, the next step is how to access the interface. Please see the next section - Accessing the interface
Section 4, Access Interface
Access to interface members
The rules for calling interface methods and accessing index indicators are the same as in the class. If the naming of the underlying member is consistent with the inherited senior member, the underlying member will override the senior member of the same name. However, since the interface supports multiple inheritance, in multiple inheritance, if the two parent interfaces contain members of the same name, this creates ambiguity (this is also one of the reasons why the multi-inheritance mechanism of classes is cancelled in C#). At this time, explicit definitions are needed:
using System ; interface ISequence { int Count { get; set; } } interface IRing { void Count(int i) ; } // /roucheng/ interface IRingSequence: ISequence, IRing { } class CTest { void Test(IRingSequence rs) { //() ; Error, Count has a duality // = ; Error, Count has a duality ((ISequence)rs).Count = ; // correct ((IRing)rs).Count() ; // Call correctly} }
In the above example, the first two statements rs .Count(1) and rs .Count = 1 will produce ambiguity, resulting in compile-time errors. Therefore, the parent interface type must be assigned to rs explicitly, which will not cause additional overhead at runtime.
Let’s look at the following example:
using System ; interface IInteger { void Add(int i) ; } interface IDouble { void Add(double d) ; } interface INumber: IInteger, IDouble {} class CMyTest { void Test(INumber Num) { // () ; mistake(.) ; // correct((IInteger)n).Add() ; // correct((IDouble)n).Add() ; // correct } }
Calling (1) will lead to ambiguity, because the parameter types of candidate overloaded methods are applicable. However, calling (1.0) is allowed because 1.0 is a floating point parameter type that is inconsistent with the parameter type of method(), and only \ is applicable at this time. However, as long as explicit assignment is added, there will never be ambiguity.
The problem of multiple inheritance of interfaces can also bring problems with member access. For example:
interface IBase { void FWay(int i) ; } interface ILeft: IBase { new void FWay (int i) ; } interface IRight: IBase { void G( ) ; } interface IDerived: ILeft, IRight { } class CTest { void Test(IDerived d) { d. FWay () ; // Call ILeft. FWay /rouch/((IBase)d). FWay () ; // Call IBase. FWay((ILeft)d). FWay () ; // Call ILeft. FWay((IRight)d). FWay () ; // Call IBase. FWay } }
In the above example, the method is overwritten by Ileft's member method FWay in the derived interface ILeft. So the call to d.FWay (1) is actually called. Although from the inheritance path of IBase->IRight->IDerived, the method is not overwritten. We just need to remember this: once a member is overwritten, all accesses to it are "intercepted" by the overwritten members.
Class-to-interface implementation
As we have said before, the interface definition does not include the implementation part of the method. Interfaces can be implemented through classes or structures. We mainly talk about implementing interfaces through classes. When implementing an interface with a class, the name of the interface must be included in the base class list in the class definition.
The following example gives an example of implementing an interface by a class. ISequence is a queue interface, providing the member method Add( ) to add an object to the tail of the queue, and IRing is a loop table interface, providing the method Insert(object obj) to insert an object into the loop, and the method returns the insertion position. The RingSquence class implements interface ISequence and interface IRing.
using System ; interface ISequence { object Add( ) ; } interface ISequence { object Add( ) ; } interface IRing { int Insert(object obj) ; } class RingSequence: ISequence, IRing { public object Add( ) {…} public int Insert(object obj) {…} }
If a class implements an interface, the class also implicitly inherits all parent interfaces of that interface, regardless of whether these parent interfaces are listed in the base class table defined by the class. See the following example:
using System ; interface IControl { void Paint( ); } interface ITextBox: IControl { void SetText(string text); } interface IListBox: IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox { }
Here, the interface IcomboBox inherits ItextBox and IlistBox. The TextBox class not only implements the interface ITextBox, but also implements the parent interface IControl of the interface ITextBox.
As we have seen earlier, a class can implement multiple interfaces. Let’s look at the following example:
interface IDataBound { void Bind(Binder b); } public class EditBox: Control, IControl, IDataBound { public void Paint( ); public void Bind(Binder b) {...} }
The class EditBox is derived from the class Control and implements Icontrol and IdataBound. In the previous example, the Paint method in the interface Icontrol and the Bind method in the IdataBound interface are both implemented using public members in the EditBox class. C# provides an optional way to implement these methods, so that classes that execute these avoid setting these members as public. Interface members can be implemented with valid names. For example, the EditBox class can be implemented in a method and introductory manner.
public class EditBox: IControl, IDataBound { void ( ) {...} void (Binder b) {...} }
Because each member is implemented by externally assigning interface members, members implemented in this way are called external interface members. External interface members can be called just through the interface. For example, the implementation of EditBox in the Paint method can be called simply by creating an Icontrol interface.
class Test { static void Main( ) { EditBox editbox = new EditBox( ); ( ); //Error: EditBox has no Paint eventIControl control = editbox; ( ); // Call EditBox's Paint event } }
In the above example, the class EditBox inherits from the Control class and implements the IControl and IDataBound interfaces at the same time. The Paint method in EditBox comes from the IControl interface, and the Bind method comes from the IDataBound interface. Both are implemented as public members in the EditBox class. Of course, in C# we can also choose not to implement interfaces as public members.
If each member clearly points out the implemented interface, the interface implemented through this approach is called an explicit interface member. In this way we rewrite the above example:
public class EditBox: IControl, IDataBound { void ( ) {…} void (Binder b) {…} }
Explicit interface members can only be called through the interface. For example:
class CTest { static void Main( ) { EditBox editbox = new EditBox( ) ; ( ) ; //Error: Different methodsIControl control = editbox; ( ) ; //Calling the Paint method of EditBox } }
The call to ( ) in the above code is wrong, because editbox itself does not provide this method. ( ) is the correct way to call.
Note: The interface itself does not provide the implementation of the defined members, it only indicates these members, and these members must rely on the support of the class or other interfaces that implement the interface.
Once we know how to access the interface, we also need to know how to implement the interface. To implement the interface of C#, please see the next section - Implementing the interface
Section 5, Implementing Interface
1. Explicitly implement interface members
To implement an interface, the class can define the Explicit interface member implementations. An explicit interface member execution body can be a definition of a method, a property, an event, or an index indicator, and the definition should be consistent with the full name corresponding to the member.
using System ; interface ICloneable { object Clone( ) ; } interface IComparable { int CompareTo(object other) ; } class ListEntry: ICloneable, IComparable { object ( ) {…} int (object other) {…} }
In the above code, and , is the explicit interface member execution body.
illustrate:
1. The explicit interface member execution body cannot be accessed through a full name in method calls, attribute access, and index indicator access. In fact, the explicit interface member executor can only be accessed through an instance of the interface and only references the member name of the interface.
2. The explicit interface member execution body cannot use any access restrictor, nor can it be added with abstract, virtual, override or static modifiers.
3. The explicit interface member executor has different access methods from other members. Because it cannot be accessed by full name in method calls, property access, and index indicator access, the explicit interface member execution body is private in a sense. But they can be accessed through interface instances and have certain public properties.
4. Only when the class is defined, the interface name is written in the base class list, and the full name, type and return type defined in the class are exactly the same as the explicit interface member execution body, the explicit interface member execution body is valid, for example:
class Shape: ICloneable { object ( ) {…} int (object other) {…} }
Using explicit interface member executors usually has two purposes:
1. Because the explicit interface member execution body cannot be accessed through the class instance, the implementation part of the interface can be separated from the public interface. If a class only uses the interface internally, and the class user will not use the interface directly, this explicit interface member execution body can play a role.
2. The explicit interface member execution body avoids confusion between interface members due to the same name. If a class wants to implement different implementations for interface members with the same name and return type, this must use the explicit interface member execution body. If there is no explicit interface member execution body, then the class cannot implement the interface members with different names and return types.
The following definition is invalid because the interface IComparable does not appear in the Shape definition time base class list.
class Shape: ICloneable { object ( ) {…} } class Ellipse: Shape { object ( ) {…} }
Defining in Ellipse is wrong, because even if Ellipse implicitly implements the interface ICloneable, ICloneable still does not appear explicitly in the base class list defined by Ellipse.
The full name of an interface member must correspond to the member defined in the interface. As in the following example, Paint's explicit interface member execution body must be written.
using System ; interface IControl { void Paint( ) ; } interface ITextBox: IControl { void SetText(string text) ; } class TextBox: ITextBox { void ( ) {…} void (string text) {…} }
A class that implements an interface can explicitly implement members of the interface. When an explicit implementation of a member, the member cannot be accessed through a class instance, but can only be accessed through an instance of the interface. An explicit interface implementation also allows programmers to inherit two interfaces that share the same member name and provide a separate implementation for each interface member.
In the following example, the dimensions of the box are displayed in both metric units and in imperial units. The Box class inherits two interfaces: IEnglishDimensions and IMetricDimensions, which represent different weight and measure systems. Both interfaces have the same member names Length and Width.
Program List 1
interface IEnglishDimensions { float Length ( ) ; float Width ( ) ; } interface IMetricDimensions { float Length ( ) ; float Width ( ) ; } class Box : IEnglishDimensions, IMetricDimensions { float lengthInches ; float widthInches ; public Box(float length, float width) { lengthInches = length ; widthInches = width ; } float ( ) { return lengthInches ; } float ( ) { return widthInches ; } float ( ) { return lengthInches * .f ; } float ( ) { return widthInches * .f ; } public static void Main( ) { //Define a real class object "myBox":: Box myBox = new Box(.f, .f); // Define an interface "eDimensions":: IEnglishDimensions eDimensions = (IEnglishDimensions) myBox; IMetricDimensions mDimensions = (IMetricDimensions) myBox; // Output: (" Length(in): {}", ( )); (" Width (in): {}", ( )); (" Length(cm): {}", ( )); (" Width (cm): {}", ( )); } }
Output: Length(in): 30, Width (in): 20, Length(cm): 76.2, Width (cm): 50.8
Code discussion: If you want the default metric to be in imperial units, please implement the Length and Width methods normally, and explicitly implement the Length and Width methods from the IMetricDimensions interface:
public float Length( ) { return lengthInches ; } public float Width( ){ return widthInches; } float ( ) { return lengthInches * .f ; } float ( ) { return widthInches * .f ; }
In this case, the imperial unit can be accessed from the class instance and the metric unit can be accessed from the interface instance:
("Length(in): {0}", ( )) ; ("Width (in): {0}", ( )) ; ("Length(cm): {0}", ( )) ; ("Width (cm): {0}", ( )) ;
2. Inheritance interface implementation
Interfaces are invariant, but this does not mean that the interface will no longer evolve. Similar to the inheritance of a class, interfaces can also be inherited and developed.
Note: Interface inheritance is different from class inheritance. First of all, class inheritance not only explains inheritance, but also implements inheritance; while interface inheritance only explains inheritance. In other words, derived classes can inherit the method implementation of the base class, while derived interfaces only inherit the member method description of the parent interface, and do not inherit the implementation of the parent interface. Secondly, class inheritance in C# only allows single inheritance, but interface inheritance allows multiple inheritance, and a child interface can have multiple parent interfaces.
Interfaces can be inherited from zero or multiple interfaces. When inheriting from multiple interfaces, use ":" followed by the inherited interface name, and use "," to separate the multiple interface names. The inherited interface should be accessible, for example, inheritance from the private type or internal type interface is not allowed. Interfaces are not allowed to be inherited directly or indirectly from themselves. Similar to the inheritance of a class, the inheritance of an interface also forms a hierarchy between interfaces.
Please see the following example:
using System ; interface IControl { void Paint( ) ; } interface ITextBox: IControl { void SetText(string text) ; } interface IListBox: IControl { void SetItems(string[] items) ; } interface IComboBox: ITextBox, IListBox { }
Inheriting an interface also inherits all members of the interface. In the example above, both interfaces ITextBox and IListBox are inherited from the interface IControl, which also inherits the Paint method of the interface IControl. The interface IComboBox inherits from the interface ITextBox and IListBox, so it should inherit the SetText method of the interface ITextBox and the SetItems method of the IListBox, as well as the Paint method of the IControl.
A class inherits all interface implementation programs provided by its base class.
Without explicitly implementing an interface, a derived class cannot change the interface map it inherits from its base class in any way. For example, in a statement
interface IControl { void Paint( ); } class Control: IControl { public void Paint( ) {...} } class TextBox: Control { new public void Paint( ) {...} }
The method Paint in TextBox hides the method Paint in Control, but does not change the mapping from , and calling Paint through class instances and interface instances will have the following impact.
Control c = new Control( ) ; TextBox t = new TextBox( ) ; IControl ic = c ; IControl it = t ; ( ) ; // Influence( ) ; ( ) ; // Influence( ) ; ( ) ; // Influence( ) ; ( ) ; // Influence( ) ;
However, when an interface method is mapped to a virtual method in a class, it is impossible for the derived class to overwrite the virtual method and change the interface's implementation function. For example, rewrite the above statement as
interface IControl { void Paint( ) ; } class Control: IControl { public virtual void Paint( ) {...} } class TextBox: Control { public override void Paint( ) {...} }
You will see the following results:
Control c = new Control( ) ; TextBox t = new TextBox( ) ; IControl ic = c ; IControl it = t ; ( ) ; // Influence( );( ) ; // Influence( );( ) ; // Influence( );( ) ; // Influence( );
Since an explicit interface member implementation program cannot be declared virtual, it is impossible to override an explicit interface member implementation program. An explicit interface member implementation program calls another method efficiently, and the other method can be declared virtual so that the derived class can override it. For example:
interface IControl { void Paint( ) ; } class Control: IControl { void ( ) { PaintControl( ); } protected virtual void PaintControl( ) {...} } class TextBox: Control { protected override void PaintControl( ) {...} }
Here, classes inherited from Control can be specialized to the implementation program by overwriting the method PaintControl.
3. Re-implement the interface
We have already introduced that derived classes can overload member methods that have been defined in the base class. Similar concepts are introduced into the implementation of class-to-interfaces, called re-implementation of interfaces. Classes that inherit the interface implementation can reimplement the interface. This interface requirement appears in the base class list defined by the class. The re-implementation of interfaces must also strictly abide by the rules for implementing interfaces for the first time. The derived interface mapping will not have any impact on the interface mapping established for the re-implementation of interfaces.
The following code gives an example of interface re-implementation:
interface IControl { void Paint( ) ; class Control: IControl void ( ) {…} class MyControl: Control, IControl public void Paint( ) {} }
In fact, it is: Control maps it to the top, but this does not affect the re-implementation in MyControl. In the re-implementation in MyControl, it is mapped on top.
When the interface is re-implemented, the inherited public member definition and the inherited explicit interface member definition participate in the interface mapping process.
using System ; interface IMethods { void F( ) ; void G( ) ; void H( ) ; void I( ) ; } class Base: IMethods { void ( ) { } void ( ) { } public void H( ) { } public void I( ) { } } class Derived: Base, IMethods { public void F( ) { } void ( ) { } }
Here, the implementation of interface IMethods in Derived maps interface methods to,,,,, and. As we mentioned earlier, when a class implements an interface, it implicitly implements all parent interfaces of that interface. Similarly, when a class reimplements an interface, it implicitly reimplements all parent interfaces of that interface.
using System ; interface IBase { void F( ) ; } interface IDerived: IBase { void G( ) ; } class C: IDerived { void ( ) { //Code to implement F...} void ( ) { //Code for implementing G...} } class D: C, IDerived { public void F( ) { //Code to implement F...} public void G( ) { //Code for implementing G... /roucheng/} }
Here, the re-implementation of IDerived also implements the re-implementation of IBase, mapping it to.
4. Mapping interface
The class must provide specific implementations for all members of the interfaces listed in the base class table. The implementation of positioning interface members in a class is called interface mapping.
Mapping mathematically represents a one-to-one functional relationship. The meaning of interface mapping is the same. Interfaces are implemented through classes. Therefore, for each member defined in the interface, a member of the class should provide it with a specific implementation.
The following conditions must be met between the members of the class and the members of the mapped interface:
1. If A and B are member methods, then the names, types, and formal parameter tables of A and B (including the number of parameters and the type of each parameter) should be consistent.
2. If A and B are attributes, then the names and types of A and B should be the same, and the accessors of A and B are also similar. But if A is not an explicit interface member executor, A allows to add its own accessor.
3. If A and B are both time, then the names and types of A and B should be the same.
4. If A and B are index indicators, the types and formal parameter tables of A and B (including the number of parameters and the type of each parameter) should be consistent. And the accessors of A and B are similar. But if A is not an explicit interface member executor, A allows to add its own accessor.
So, for an interface member, how to determine which class member is implemented? That is, which class members are mapped by an interface member? Here, let’s describe the process of interface mapping. Suppose that class C implements an interface IInterface, and Member is a member of the interface IInterface. When locating who implements the interface member, that is, Member's mapping process is as follows:
1. If there is an explicit interface member execution body in C that corresponds to interface IInterface and its member Member, it implements Member members.
2. If the condition (1) is not satisfied and there is a non-static public member in C, and the member corresponds to the interface member Member, it will implement the Member member.
3. If the above conditions are still not met, then look for a base class D of C in the base class list defined by class C and use D instead of C.
4. Repeat steps 1-3, traversing all direct base classes and non-direct base classes in C until a member of a class that meets the conditions is found.
5. If it is still not found, an error is reported.
The following is an example of calling the base class method to implement interface members. Class Class2 implements interface Interface1. Members of the base class Class1 of Class2 also participate in the interface mapping. That is to say, when class Class2 implements interface Interface1, it uses the member method F provided by class Class1 to implement the member method F of interface Interface1:
interface Interface1 { void F( ) ; } class Class1 { public void F( ) { } public void G( ) { } } class Class2: Class1, Interface1 { new public void G( ) {} }
Note: The members of an interface include members that it defines itself, and include all members defined by the parent interface of the interface. When mapping an interface, not only should all members explicitly defined in the interface definition body be mapped, but all members of the interface implicitly inherited from the parent interface must be mapped.
When mapping interfaces, you should also pay attention to the following two points:
1. When deciding which member of the class implements the interface member, the explicitly stated interface members in the class are implemented first over other members.
2. Members using Private, protected and static modifiers cannot participate in implementing interface mapping. For example:
interface ICloneable { object Clone( ) ; } class C: ICloneable { object ( ) {…} public object Clone( ) {…} }
Members in the example are the implementors of Clone, a member called interface ICloneable, because it is an explicitly stated interface member and has higher priority than other members.
If a class implements two or more interfaces with the same name, type, and parameter types, then one member of the class may implement all of these interface members:
interface IControl { void Paint( ) ; } interface IForm { void Paint( ) ; } class Page: IControl, IForm { public void Paint( ) {…} }
Here, the methods Paint of the interface IControl and IForm are mapped to the Paint method in the class Page. Of course, these two methods can also be implemented separately using explicit interface members:
interface IControl { void Paint( ) ; } interface IForm { void Paint( ) ; } class Page: IControl, IForm { public void ( ) { //Specific interface implementation code } public void ( ) { //Specific interface implementation code / } }
Both of the above are correct. But if an interface member overrides a member of the parent interface in inheritance, then the implementation of that interface member may have to map to the explicit interface member execution body. See the following example:
interface IBase { int P { get; } } interface IDerived: IBase { new int P( ) ; }
Interface IDerived inherits from interface IBase, and the member method of interface IDerived overwrites the member method of the parent interface. Because there are two interface members with the same name at this time, if the implementation of these two interface members does not use an explicit interface member execution body, the compiler will not be able to distinguish the interface mapping. Therefore, if a class wants to implement interface IDerived, at least one explicit interface member execution body must be defined in the class. It is reasonable to use the following writing methods:
//1: Use explicit interface member execution body to implement both interface members
class C: IDerived { int get { //Specific interface implementation code } int ( ){ //Specific interface implementation code } }
//2: Use explicit interface member execution body to implement the interface members of Ibase
class C: IDerived { int get {//Specific interface implementation code} public int P( ){ //Specific interface implementation code } }
//3: Use explicit interface member execution body to implement the interface members of IDerived
class C: IDerived{ public int P get {//Specific interface implementation code} int ( ){ //Specific interface implementation code} }
Another case is that if a class implements multiple interfaces and these interfaces have the same parent interface, this parent interface is only allowed to be implemented once.
using System ; interface IControl { void Paint( ) ; interface ITextBox: IControl { void SetText(string text) ; } interface IListBox: IControl { void SetItems(string[] items) ; } class ComboBox: IControl, ITextBox, IListBox { void ( ) {…} void (string text) {…} void (string[] items) {…} }
In the example above, the ComboBox class implements three interfaces: IControl, ITextBox and IListBox. If you think that ComboBox not only implements the IControl interface, but also implements their parent interface IControl while implementing ITextBox and IListBox. In fact, the implementation of interface ITextBox and IListBox shares the implementation of interface IControl.
We have a comprehensive understanding of C# interfaces and basically master how to apply C# interface programming. But in fact, C# is not only used on the .NET platform, it also supports the previous COM, which can implement the conversion from COM class to .NET class, such as C# calling API. To learn about this, please see the next section - Interface conversion.
Section 6, Interface Conversion
C# supports not only the .Net platform, but also the COM platform. To support COM and .Net, C# contains a unique language feature called attributes. A property is actually a C# class that provides meta information by modifying the source code. Properties enable C# to support specific technologies such as COM and .Net without interfering with the language specification itself. C# provides attribute classes that convert COM interfaces to C# interfaces. Other property classes convert COM classes to C# classes. No IDL or class factory is required to perform these transformations.
Any COM component deployed now can be used in interface transformation. Typically, the adjustments required are made completely automatic.
In particular, COM components can be accessed from the .NET framework using runtime callable wrapper (RCW). This wrapper converts the COM interface provided by the COM component into an interface compatible with the .NET framework. For OLE automation interfaces, RCWs can be automatically generated from the type library; for non-OLE automation interfaces, developers can write custom RCWs that manually map types provided by the COM interface to types compatible with the .NET framework.
Reference COM components using ComImport
COM Interop provides access to existing COM components without modifying the original components. Using ComImport to reference COM components often includes the following issues:
1. Create a COM object.
2. Determine whether the COM interface is implemented by an object.
3. Call the method on the COM interface.
4. Implement objects and interfaces that can be called by the COM client.
Create COM class wrappers
To make C# code reference COM objects and interfaces, you need to include the definition of the COM interface in C#. The easiest way to do this is to use (Type Library Importer), a command-line tool included in the .NET Framework SDK. TlbImp converts the COM type library to .NET framework metadata, effectively creating a managed wrapper that can be called from any managed language. .NET framework metadata created with TlbImp can be included in the C# build with the /R compiler option. If you are using a Visual Studio development environment, you simply add a reference to the COM type library and this conversion will be done automatically for you.
TlbImp performs the following conversion:
1. COM coclass is converted to a C# class with a parameterless constructor.
2. The COM structure is converted into a C# structure with common fields.
A good way to check TlbImp output is to run the .NET Framework SDK command line tool (Microsoft Intermediate Language Disassembler) to view the conversion results.
Although TlbImp is the preferred method for converting COM definitions to C#, it is not available at any time (for example, it cannot be used without a COM-defined type library or when TlbImp cannot handle definitions in a type library). In these cases, another approach is to manually define the COM definition in the C# source code using the C# attribute. After creating a C# source map, you can generate a managed wrapper by simply compiling the C# source code.
The main attributes to understand when performing COM mapping include:
1. ComImport: It marks the class as an externally implemented COM class.
2. Guid: It is used to specify a universal unique identifier (UUID) for a class or interface.
3. InterfaceType, which specifies whether the interface is derived from IUnknown or IDispatch.
4. PreserveSig, which specifies whether the native return value should be converted from HRESULT to .NET framework exception.
Statement COM coclass
COM coclass is represented as a class in C#. These classes must have the ComImport property associated with them. The following restrictions apply to these classes:
1. Classes cannot be inherited from any other class.
2. Classes cannot implement any interface.
4. The class must also have a Guid property that sets a globally unique identifier (GUID) for it.
The following example declares a coclass in C#:
// Declare a COM class FilgraphManager[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] class FilgraphManager { }
The C# compiler will add a parameterless constructor that can be called to create an instance of COM coclass.
Create COM object
COM coclass is represented in C# as a class with a parameterless constructor. Creating an instance of this class using the new operator is equivalent to calling CoCreateInstance in C#. This class can be instantiated easily using the class defined above:
class MainClass { public static void Main() { FilgraphManager filg = new FilgraphManager(); } }
Declare COM interface
The COM interface is represented in C# as an interface with ComImport and Guid attributes. It cannot contain any interfaces in its base interface list, and must declare interface member functions in the order in which the method appears in the COM interface.
A COM interface declared in C# must contain declarations for all members of its base interface, except for members of IUnknown and IDispatch (the .NET framework will automatically add these members). The COM interface derived from IDispatch must be marked with the InterfaceType property.
When calling a COM interface method from C# code, the common language runner must marshal the parameters and return values passed between the COM object. There is a default type for each .NET framework type, and the common language runtime uses this default type to marshall it between COM calls. For example, the default marshaling of C# string values is marshaling to native type LPTSTR (a pointer to the TCHAR character buffer). The default marshals can be overridden in the C# declaration of the COM interface.
In COM, a common way to return success or failure is to return an HRESULT and have an out parameter in MIDL marked "retval" for the actual return value of the method. In C# (and .NET frameworks), the standard way to indicate that an error has occurred is to raise an exception.
By default, the .NET framework provides automatic mapping between the two exception handling types for the COM interface method it calls.
The return value changes to the signature of the parameter marked retval (void if the method has no parameter marked retval).
The parameter marked retval is stripped from the method's parameter list.
Any non-successful return value will cause an exception to be raised.
This example shows the COM interface declared with MIDL and the same interface declared with C# (note that these methods use COM error handling methods).
The following is the C# program for interface conversion:
using ; // Declare a COM interface IMediaControl [Guid("AB-AD-CE-BA-AFBA"), InterfaceType()] interface IMediaControl // No base interface can be listed here { void Run(); void Pause(); void Stop(); void GetState( [In] int msTimeout, [Out] out int pfs); void RenderFile( [In, MarshalAs()] string strFilename); void AddSourceFilter( [In, MarshalAs()] string strFilename, [Out, MarshalAs()] out object ppUnk); [return : MarshalAs()] object FilterCollection(); [return : MarshalAs()] object RegFilterCollection(); void StopWhenReady(); }
To prevent HRESULT from being translated into COMException, attach the PreserveSig(true) property to the method in the C# declaration.
Here is a program that uses C# to map COM objects on media players.
Program List 2
using System; using ; namespace QuartzTypeLib { //Declare a COM interface IMediaControl, which comes from the COM class of the media player[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"), InterfaceType()] interface IMediaControl { //List the interface membersvoid Run(); void Pause(); void Stop(); void GetState( [In] int msTimeout, [Out] out int pfs); void RenderFile( [In, MarshalAs()] string strFilename); void AddSourceFilter( [In, MarshalAs()] string strFilename, [Out, MarshalAs()] out object ppUnk); [return: MarshalAs()] object FilterCollection(); [return: MarshalAs()] object RegFilterCollection(); void StopWhenReady(); } //Declare a COM class:[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] class FilgraphManager //This class cannot inherit other base classes or interfaces{ //There is no code here, the system will automatically add a default constructor} } class MainClass { public static void Main(string[] args) { //Command line parameters:if ( != 1) { DisplayUsage(); return; } String filename = args[0]; if (("/?")) { DisplayUsage(); return; } // Declare the real class object of FilgraphManager: graphManager =new (); //Declare the real class object of IMediaControl:: mc =()graphManager; // Call COM method:(filename); //Run files.(); //Doing temporarily.("Press Enter to continue."); (); } private static void DisplayUsage() { // show("Media Player: Play AVI Files."); ("How to use: File name"); } }
Running example:
To display a video sample, use the following command:
interop2 %windir%/
This will display the video on the screen until the ENTER key is pressed to stop.
Using Win32 API with DllImport in .NET Framework
.NET framework programs can access native code bases through static DLL entry points. The DllImport property specifies the dll location of an implementation containing an external method.
The DllImport property is defined as follows:
namespace { [AttributeUsage()] public class DllImportAttribute: { public DllImportAttribute(string dllName) {...} public CallingConvention CallingConvention; public CharSet CharSet; public string EntryPoint; public bool ExactSpelling; public bool PreserveSig; public bool SetLastError; public string Value { get {...} } } }
illustrate:
1. DllImport can only be placed on the method declaration.
2. DllImport has a single positioning parameter: specify the dllName parameter containing the dll name of the imported method.
3. DllImport has five named parameters:
a. The CallingConvention parameter indicates the calling convention of the entry point. If CallingConvention is not specified, the default value is used.
b. The CharSet parameter indicates the character set used in the entry point. If CharSet is not specified, the default value is used.
c. The EntryPoint parameter gives the name of the entry point in the dll. If EntryPoint is not specified, the name of the method itself is used.
d. ExactSpelling parameter indicates whether the EntryPoint must exactly match the spelling of the indicated entry point. If ExactSpelling is not specified, the default value is false.
e. PreserveSig parameters indicate whether the signature of the method should be retained or converted. When the signature is converted, it is converted to a signature with an additional output parameter named retval with the HRESULT return value and the return value. If PreserveSig is not specified, the default value is true.
f, SetLastError parameter indicates whether the method retains Win32 "Previous Error". If SetLastError is not specified, the default value is false.
4. It is a one-time attribute class.
5. In addition, methods that are modified with the DllImport property must have an extern modifier.
Here is an example of C# calling the Win32 MessageBox function:
using System; using ; class MainApp { //Reference the class through DllImport. MessageBox comes from class [DllImport("", EntryPoint="MessageBox")] public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType); public static void Main() { MessageBox( , "Hello, this is PInvoke!", ".NET", ); } }
Almost all object-oriented programming languages use the concept of abstract classes, which provide greater flexibility for implementing abstract things. C# is no exception. C# deepens the application of abstract classes through the technology of overwriting virtual interfaces. To learn about this, please see the next section - Coverage Virtual Interface
Section 7: Covering virtual interface
Sometimes we need to express an abstract thing, which is a generalization of something, but we cannot really see it appearing as an entity in front of our eyes. For this reason, the object-oriented programming language has the concept of abstract classes. As an object-oriented language, C# will inevitably introduce the concept of abstract classes. Interfaces and abstract classes allow you to create definitions of component interactions. Through an interface, you can specify the methods that a component must implement, but not actually specify how to implement them. Abstract classes allow you to create definitions of behavior while providing some common implementations for inheriting classes. Interfaces and abstract classes are useful tools for implementing polymorphic behavior in components.
An abstract class must provide an implementer for all members of the interface listed in the class's base class list. However, an abstract class is allowed to map interface methods into abstract methods. For example
interface IMethods { void F(); void G(); } abstract class C: IMethods { public abstract void F(); public abstract void G(); }
Here, the IMethods implementation function maps F and G into abstract methods, which must be overwritten in non-abstract classes derived from C.
Note that explicit interface member implementation functions cannot be abstract, but explicit interface member implementation functions can of course call abstract methods. For example
interface IMethods { void F(); void G(); } abstract class C: IMethods { void () { FF(); } void () { GG(); } protected abstract void FF(); protected abstract void GG(); }