- •Contents at a Glance
- •Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •C# and the .NET Framework
- •Before .NET
- •Windows Programming in the Late 1990s
- •Goals for the Next-Generation Platform Services
- •Enter Microsoft .NET
- •Components of the .NET Framework
- •An Improved Programming Environment
- •Object-Oriented Development Environment
- •Automatic Garbage Collection
- •Interoperability
- •No COM Required
- •Simplified Deployment
- •Type Safety
- •The Base Class Library
- •Compiling to the Common Intermediate Language
- •Compiling to Native Code and Execution
- •Overview of Compilation and Execution
- •The Common Language Runtime
- •The Common Language Infrastructure
- •Important Parts of the CLI
- •Common Type System (CTS)
- •Common Language Specification (CLS)
- •Review of the Acronyms
- •Overview of C# Programming
- •A Simple C# Program
- •More About SimpleProgram
- •Identifiers and Keywords
- •Naming Conventions
- •Keywords
- •Main: The Starting Point of a Program
- •Whitespace
- •Statements
- •Simple Statements
- •Blocks
- •Text Output from a Program
- •Write
- •WriteLine
- •The Format String
- •Multiple Markers and Values
- •Comments: Annotating the Code
- •More About Comments
- •Documentation Comments
- •Summary of Comment Types
- •Types, Storage, and Variables
- •A C# Program Is a Set of Type Declarations
- •A Type Is a Template
- •Instantiating a Type
- •Data Members and Function Members
- •Types of Members
- •Predefined Types
- •More About the Predefined Types
- •User-Defined Types
- •The Stack and the Heap
- •The Stack
- •Facts About Stacks
- •The Heap
- •Value Types and Reference Types
- •Storing Members of a Reference Type Object
- •Categorizing the C# Types
- •Variables
- •Variable Declarations
- •Variable Initializers
- •Automatic Initialization
- •Multiple-Variable Declarations
- •Using the Value of a Variable
- •Static Typing and the dynamic Keyword
- •Nullable Types
- •Creating a Nullable Type
- •Assigning to a Nullable Type
- •Classes: The Basics
- •Overview of Classes
- •A Class Is an Active Data Structure
- •Programs and Classes: A Quick Example
- •Declaring a Class
- •Class Members
- •Fields
- •Explicit and Implicit Field Initialization
- •Declarations with Multiple Fields
- •Methods
- •Creating Variables and Instances of a Class
- •Allocating Memory for the Data
- •Combining the Steps
- •Instance Members
- •Access Modifiers
- •Private and Public Access
- •Depicting Public and Private Access
- •Example of Member Access
- •Accessing Members from Inside the Class
- •Accessing Members from Outside the Class
- •Putting It All Together
- •Methods
- •The Structure of a Method
- •Code Execution in the Method Body
- •Local Variables
- •Type Inference and the var Keyword
- •Local Variables Inside Nested Blocks
- •Local Constants
- •Flow of Control
- •Method Invocations
- •Return Values
- •The Return Statement and Void Methods
- •Parameters
- •Formal Parameters
- •Actual Parameters
- •An Example of Methods with Positional Input Parameters
- •Value Parameters
- •Reference Parameters
- •Output Parameters
- •Parameter Arrays
- •Method Invocation
- •Expanded Form
- •Arrays As Actual Parameters
- •Summary of Parameter Types
- •Method Overloading
- •Named Parameters
- •Optional Parameters
- •Stack Frames
- •Recursion
- •More About Classes
- •Class Members
- •Order of Member Modifiers
- •Instance Class Members
- •Static Fields
- •Accessing Static Members from Outside the Class
- •Example of a Static Field
- •Lifetimes of Static Members
- •Static Function Members
- •Other Static Class Member Types
- •Member Constants
- •Constants Are Like Statics
- •Properties
- •Property Declarations and Accessors
- •A Property Example
- •Using a Property
- •Properties and Associated Fields
- •Performing Other Calculations
- •Read-Only and Write-Only Properties
- •An Example of a Computed, Read-Only Property
- •Example of Properties and Databases
- •Properties vs. Public Fields
- •Automatically Implemented Properties
- •Static Properties
- •Instance Constructors
- •Constructors with Parameters
- •Default Constructors
- •Static Constructors
- •Example of a Static Constructor
- •Accessibility of Constructors
- •Object Initializers
- •Destructors
- •Calling the Destructor
- •The Standard Dispose Pattern
- •Comparing Constructors and Destructors
- •The readonly Modifier
- •The this Keyword
- •Indexers
- •What Is an Indexer?
- •Indexers and Properties
- •Declaring an Indexer
- •The Indexer set Accessor
- •The Indexer get Accessor
- •More About Indexers
- •Declaring the Indexer for the Employee Example
- •Another Indexer Example
- •Indexer Overloading
- •Access Modifiers on Accessors
- •Partial Classes and Partial Types
- •Partial Methods
- •Classes and Inheritance
- •Class Inheritance
- •Accessing the Inherited Members
- •All Classes Are Derived from Class object
- •Hiding Members of a Base Class
- •Base Access
- •Using References to a Base Class
- •Virtual and Override Methods
- •Overriding a Method Marked override
- •Case 1: Declaring Print with override
- •Case 2: Declaring Print with new
- •Overriding Other Member Types
- •Constructor Execution
- •Constructor Initializers
- •Class Access Modifiers
- •Inheritance Between Assemblies
- •Member Access Modifiers
- •Regions Accessing a Member
- •Public Member Accessibility
- •Private Member Accessibility
- •Protected Member Accessibility
- •Internal Member Accessibility
- •Protected Internal Member Accessibility
- •Summary of Member Access Modifiers
- •Abstract Members
- •Abstract Classes
- •Example of an Abstract Class and an Abstract Method
- •Another Example of an Abstract Class
- •Sealed Classes
- •Static Classes
- •Extension Methods
- •Expressions and Operators
- •Expressions
- •Literals
- •Integer Literals
- •Real Literals
- •Character Literals
- •String Literals
- •Order of Evaluation
- •Precedence
- •Associativity
- •Simple Arithmetic Operators
- •The Remainder Operator
- •Relational and Equality Comparison Operators
- •Comparison and Equality Operations
- •Increment and Decrement Operators
- •Conditional Logical Operators
- •Logical Operators
- •Shift Operators
- •Assignment Operators
- •Compound Assignment
- •The Conditional Operator
- •Unary Arithmetic Operators
- •User-Defined Type Conversions
- •Explicit Conversion and the Cast Operator
- •Operator Overloading
- •Restrictions on Operator Overloading
- •Example of Operator Overloading
- •The typeof Operator
- •Other Operators
- •Statements
- •What Are Statements?
- •Expression Statements
- •Flow-of-Control Statements
- •The if Statement
- •The if . . . else Statement
- •The switch Statement
- •A Switch Example
- •More on the switch Statement
- •Switch Labels
- •The while Loop
- •The do Loop
- •The for Loop
- •The Scope of Variables in a for Statement
- •Multiple Expressions in the Initializer and Iteration Expression
- •Jump Statements
- •The break Statement
- •The continue Statement
- •Labeled Statements
- •Labels
- •The Scope of Labeled Statements
- •The goto Statement
- •The goto Statement Inside a switch Statement
- •The using Statement
- •Packaging Use of the Resource
- •Example of the using Statement
- •Multiple Resources and Nesting
- •Another Form of the using Statement
- •Other Statements
- •Namespaces and Assemblies
- •Referencing Other Assemblies
- •The mscorlib Library
- •Namespaces
- •Namespace Names
- •More About Namespaces
- •Namespaces Spread Across Files
- •Nesting Namespaces
- •The using Directives
- •The using Namespace Directive
- •The using Alias Directive
- •The Structure of an Assembly
- •The Identity of an Assembly
- •Strongly Named Assemblies
- •Creating a Strongly Named Assembly
- •Private Deployment of an Assembly
- •Shared Assemblies and the GAC
- •Installing Assemblies into the GAC
- •Side-by-Side Execution in the GAC
- •Configuration Files
- •Delayed Signing
- •Exceptions
- •What Are Exceptions?
- •The try Statement
- •Handling the Exception
- •The Exception Classes
- •The catch Clause
- •Examples Using Specific catch Clauses
- •The catch Clauses Section
- •The finally Block
- •Finding a Handler for an Exception
- •Searching Further
- •General Algorithm
- •Example of Searching Down the Call Stack
- •Throwing Exceptions
- •Throwing Without an Exception Object
- •Structs
- •What Are Structs?
- •Structs Are Value Types
- •Assigning to a Struct
- •Constructors and Destructors
- •Instance Constructors
- •Static Constructors
- •Summary of Constructors and Destructors
- •Field Initializers Are Not Allowed
- •Structs Are Sealed
- •Boxing and Unboxing
- •Structs As Return Values and Parameters
- •Additional Information About Structs
- •Enumerations
- •Enumerations
- •Setting the Underlying Type and Explicit Values
- •Implicit Member Numbering
- •Bit Flags
- •The Flags Attribute
- •Example Using Bit Flags
- •More About Enums
- •Arrays
- •Arrays
- •Definitions
- •Important Details
- •Types of Arrays
- •An Array As an Object
- •One-Dimensional and Rectangular Arrays
- •Declaring a One-Dimensional Array or a Rectangular Array
- •Instantiating a One-Dimensional or Rectangular Array
- •Accessing Array Elements
- •Initializing an Array
- •Explicit Initialization of One-Dimensional Arrays
- •Explicit Initialization of Rectangular Arrays
- •Syntax Points for Initializing Rectangular Arrays
- •Shortcut Syntax
- •Implicitly Typed Arrays
- •Putting It All Together
- •Jagged Arrays
- •Declaring a Jagged Array
- •Shortcut Instantiation
- •Instantiating a Jagged Array
- •Subarrays in Jagged Arrays
- •Comparing Rectangular and Jagged Arrays
- •The foreach Statement
- •The Iteration Variable Is Read-Only
- •The foreach Statement with Multidimensional Arrays
- •Example with a Rectangular Array
- •Example with a Jagged Array
- •Array Covariance
- •Useful Inherited Array Members
- •The Clone Method
- •Comparing Array Types
- •Delegates
- •What Is a Delegate?
- •Declaring the Delegate Type
- •Creating the Delegate Object
- •Assigning Delegates
- •Combining Delegates
- •Adding Methods to Delegates
- •Removing Methods from a Delegate
- •Invoking a Delegate
- •Delegate Example
- •Invoking Delegates with Return Values
- •Invoking Delegates with Reference Parameters
- •Anonymous Methods
- •Using Anonymous Methods
- •Syntax of Anonymous Methods
- •Return Type
- •Parameters
- •params Parameters
- •Scope of Variables and Parameters
- •Outer Variables
- •Extension of Captured Variable’s Lifetime
- •Lambda Expressions
- •Events
- •Events Are Like Delegates
- •An Event Has a Private Delegate
- •Overview of Source Code Components
- •Declaring an Event
- •An Event Is a Member
- •The Delegate Type and EventHandler
- •Raising an Event
- •Subscribing to an Event
- •Removing Event Handlers
- •Standard Event Usage
- •Using the EventArgs Class
- •Passing Data by Extending EventArgs
- •Using the Custom Delegate
- •The MyTimerClass Code
- •Event Accessors
- •Interfaces
- •What Is an Interface?
- •Example Using the IComparable Interface
- •Declaring an Interface
- •Implementing an Interface
- •Example with a Simple Interface
- •An Interface Is a Reference Type
- •Using the as Operator with Interfaces
- •Implementing Multiple Interfaces
- •Implementing Interfaces with Duplicate Members
- •References to Multiple Interfaces
- •An Inherited Member As an Implementation
- •Explicit Interface Member Implementations
- •Accessing Explicit Interface Member Implementations
- •Interfaces Can Inherit Interfaces
- •Example of Different Classes Implementing an Interface
- •Conversions
- •What Are Conversions?
- •Implicit Conversions
- •Explicit Conversions and Casting
- •Casting
- •Types of Conversions
- •Numeric Conversions
- •Implicit Numeric Conversions
- •Overflow Checking Context
- •The checked and unchecked Operators
- •The checked and unchecked Statements
- •Explicit Numeric Conversions
- •Integral to Integral
- •float or double to Integral
- •decimal to Integral
- •double to float
- •float or double to decimal
- •decimal to float or double
- •Reference Conversions
- •Implicit Reference Conversions
- •Explicit Reference Conversions
- •Valid Explicit Reference Conversions
- •Boxing Conversions
- •Boxing Creates a Copy
- •The Boxing Conversions
- •Unboxing Conversions
- •The Unboxing Conversions
- •User-Defined Conversions
- •Constraints on User-Defined Conversions
- •Example of a User-Defined Conversion
- •Evaluating User-Defined Conversions
- •Example of a Multistep User-Defined Conversion
- •The is Operator
- •The as Operator
- •Generics
- •What Are Generics?
- •A Stack Example
- •Generics in C#
- •Continuing with the Stack Example
- •Generic Classes
- •Declaring a Generic Class
- •Creating a Constructed Type
- •Creating Variables and Instances
- •The Stack Example Using Generics
- •Comparing the Generic and Nongeneric Stack
- •Constraints on Type Parameters
- •Where Clauses
- •Constraint Types and Order
- •Generic Methods
- •Declaring a Generic Method
- •Invoking a Generic Method
- •Inferring Types
- •Example of a Generic Method
- •Extension Methods with Generic Classes
- •Generic Structs
- •Generic Delegates
- •Another Generic Delegate Example
- •Generic Interfaces
- •An Example Using Generic Interfaces
- •Generic Interface Implementations Must Be Unique
- •Covariance and Contravariance in Generics
- •Covariance and Contravariance in Interfaces
- •More About Variance
- •Enumerators and Iterators
- •Enumerators and Enumerable Types
- •Using the foreach Statement
- •Types of Enumerators
- •Using the IEnumerator Interface
- •Declaring an IEnumerator Enumerator
- •The IEnumerable Interface
- •Example Using IEnumerable and IEnumerator
- •The Noninterface Enumerator
- •The Generic Enumeration Interfaces
- •The IEnumerator<T> Interface
- •The IEnumerable<T> Interface
- •Iterators
- •Iterator Blocks
- •Using an Iterator to Create an Enumerator
- •Using an Iterator to Create an Enumerable
- •Common Iterator Patterns
- •Producing Enumerables and Enumerators
- •Producing Multiple Enumerables
- •Producing Multiple Enumerators
- •Behind the Scenes with Iterators
- •Introduction to LINQ
- •What Is LINQ?
- •LINQ Providers
- •Anonymous Types
- •Query Syntax and Method Syntax
- •Query Variables
- •The Structure of Query Expressions
- •The from Clause
- •The join Clause
- •What Is a Join?
- •The from . . . let . . . where Section in the Query Body
- •The from Clause
- •The let Clause
- •The where Clause
- •The orderby Clause
- •The select . . . group Clause
- •Anonymous Types in Queries
- •The group Clause
- •Query Continuation
- •The Standard Query Operators
- •Signatures of the Standard Query Operators
- •Delegates As Parameters
- •The LINQ Predefined Delegate Types
- •Example Using a Delegate Parameter
- •Example Using a Lambda Expression Parameter
- •LINQ to XML
- •Markup Languages
- •XML Basics
- •The XML Classes
- •Creating, Saving, Loading, and Displaying an XML Document
- •Creating an XML Tree
- •Using Values from the XML Tree
- •Adding Nodes and Manipulating XML
- •Working with XML Attributes
- •Other Types of Nodes
- •XComment
- •XDeclaration
- •XProcessingInstruction
- •Using LINQ Queries with LINQ to XML
- •Introduction to Asynchronous Programming
- •Processes, Threads, and Asynchronous Programming
- •Multithreading Considerations
- •The Complexity of Multithreading
- •Parallel Loops
- •The BackgroundWorker Class
- •Example Code Using the BackgroundWorker Class
- •Example of the BackgroundWorker Class in a WPF Program
- •Asynchronous Programming Patterns
- •BeginInvoke and EndInvoke
- •The Wait-Until-Done Pattern
- •The AsyncResult Class
- •The Polling Pattern
- •The Callback Pattern
- •The Callback Method
- •Calling EndInvoke Inside the Callback Method
- •Timers
- •Preprocessor Directives
- •What Are Preprocessor Directives?
- •General Rules
- •The #define and #undef Directives
- •Conditional Compilation
- •The Conditional Compilation Constructs
- •Diagnostic Directives
- •Line Number Directives
- •Region Directives
- •The #pragma warning Directive
- •Reflection and Attributes
- •Metadata and Reflection
- •The Type Class
- •Getting a Type Object
- •What Is an Attribute?
- •Applying an Attribute
- •Predefined, Reserved Attributes
- •The Obsolete Attribute
- •The Conditional Attribute
- •Example of the Conditional Attribute
- •Predefined Attributes
- •More About Applying Attributes
- •Multiple Attributes
- •Other Types of Targets
- •Global Attributes
- •Custom Attributes
- •Declaring a Custom Attribute
- •Using Attribute Constructors
- •Specifying the Constructor
- •Using the Constructor
- •Positional and Named Parameters in Constructors
- •Restricting the Usage of an Attribute
- •The Constructor for AttributeUsage
- •Suggested Practices for Custom Attributes
- •Accessing an Attribute
- •Using the IsDefined Method
- •Using the GetCustomAttributes Method
- •Other Topics
- •Overview
- •Strings
- •Using Class StringBuilder
- •Formatting Numeric Strings
- •The Alignment Specifier
- •The Format Component
- •Standard Numeric Format Specifiers
- •Parsing Strings to Data Values
- •More About the Nullable Types
- •The Null Coalescing Operator
- •Using Nullable User-Defined Types
- •Nullable<T>
- •Method Main
- •Accessibility of Main
- •Documentation Comments
- •Inserting Documentation Comments
- •Using Other XML Tags
- •Nested Types
- •Example of a Nested Class
- •Visibility and Nested Types
- •Interoperating with COM
- •Index
CHAPTER 7 CLASSES AND INHERITANCE
Sealed Classes
In the previous section, you saw that an abstract class must be used as a base class—it cannot be instantiated as a stand-alone class object. The opposite is true of a sealed class.
•A sealed class can be instantiated only as a stand-alone class object—it cannot be used as a base class.
•A sealed class is labeled with the sealed modifier.
For example, the following class is a sealed class. Any attempt to use it as the base class of another class will produce a compile error.
Keyword
↓
sealed class MyClass
{
...
}
195
CHAPTER 7 CLASSES AND INHERITANCE
Static Classes
A static class is a class where all the members are static. Static classes are used to group data and functions that are not affected by instance data. A common use of a static class might be to create a math library containing sets of mathematical methods and values.
The important things to know about static classes are the following:
•The class itself must be marked static.
•All the members of the class must be static.
•The class can have a static constructor, but it cannot have an instance constructor, since you cannot create an instance of the class.
•Static classes are implicitly sealed. That is, you cannot inherit from a static class.
You access the members of a static class just as you would access any static member, by using the class name and the member name.
The following code shows an example of a static class:
Class must be marked static
↓ |
|
|
|
|
|||
static public class MyMath |
|
|
|
||||
{ |
|
|
|
|
|
|
|
public static float PI = 3.14f; |
|
|
|
||||
public static bool IsOdd(int x) |
|
|
|
||||
|
↑ |
|
{ return x % 2 == 1; } |
|
|
|
|
Members must be static |
|
|
|
||||
|
↓ |
|
|
|
|
|
|
public static int Times2(int x) |
|
|
|
||||
} |
|
|
|
{ return 2 * x; } |
|
|
|
|
|
|
|
|
|
|
|
class Program |
|
|
|
|
|||
{ |
|
|
|
|
|
|
|
static void Main( ) |
Use class name and member name. |
||||||
{ |
|
|
|
|
|||
int val = 3; |
|
|
↓ |
|
|||
Console.WriteLine("{0} is odd is {1}.", val, |
MyMath.IsOdd(val)); |
||||||
Console.WriteLine("{0} * 2 = {1}.", |
val, |
MyMath.Times2(val)); |
|||||
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
This code produces the following output:
3 is odd is True.
3 * 2 = 6.
196
CHAPTER 7 CLASSES AND INHERITANCE
Extension Methods
So far in this text, every method you’ve seen has been associated with the class in which it is declared. The extension method feature introduced in C# 3.0 extends that boundary, allowing you to write methods associated with classes other than the class in which they are declared.
To see how you might use this feature, take a look at the following code. It contains class MyData, which stores three values of type double, and contains a constructor and a method called Sum, which returns the sum of the three stored values.
class MyData |
|
|
{ |
|
|
private double |
D1; |
// Fields |
private double |
D2; |
|
private double |
D3; |
|
public MyData(double d1, double d2, double d3) |
// Constructor |
|
{ |
|
|
D1 = d1; D2 |
= d2; D3 = d3; |
|
} |
|
|
public double Sum() |
// Method Sum |
|
{ |
|
|
return D1 + |
D2 + D3; |
|
} |
|
|
} |
|
|
This is a pretty limited class, but suppose it would be more useful if it contained another method, which returned the average of the three data points. With what you know so far about classes, there are several ways you might implement the additional functionality:
•If you have the source code and can modify the class, you could, of course, just add the new method to the class.
•If, however, you can’t modify the class—for example, if the class is in a third-party class library— then, as long as it isn’t sealed, you could use it as a base class and implement the additional method in a class derived from it.
If, however, you don’t have access to the code or the class is sealed or there is some other design reason that neither of these solutions will work, then you will have to write a method in another class that uses the publicly available members of the class.
197
CHAPTER 7 CLASSES AND INHERITANCE
For example, you might write a class like the one in the following code. The code contains a static class called ExtendMyData, which contains a static method called Average, which implements the additional functionality. Notice that the method takes an instance of MyData as a parameter.
static class ExtendMyData |
Instance of MyData class |
||||||
{ |
|
|
↓ |
|
|
|
|
public static double Average( MyData md ) |
|
|
|||||
{ |
|
|
|
|
|
|
|
|
return md.Sum() / 3; |
|
|
|
|
|
|
} |
↑ |
|
|
|
|
|
|
} Use the instance of MyData. |
|
|
|
|
|
|
|
class Program |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
static void Main() |
|
|
|
|
Instance of MyData |
||
{ |
|
|
|
|
|
||
|
MyData md = new MyData(3, 4, 5); |
↓ |
|||||
|
Console.WriteLine("Average: {0}", ExtendMyData.Average(md)); |
||||||
} |
|
|
|
|
|
↑ |
|
} |
|
|
|
|
|
Call the static method. |
This code produces the following output:
Average: 4
Although this is a perfectly fine solution, it would be more elegant if you could call the method on the class instance itself, rather than creating an instance of another class to act on it. The following two lines of code illustrate the difference. The first uses the method just shown—invoking a static method on an instance of another class. The second shows the form we would like to use—invoking an instance method on the object itself.
ExtendMyData.Average( md ) |
// |
Static invocation form |
md.Average(); |
// |
Instance invocation form |
Extension methods allow you to use the second form, even though the first form would be the normal way of writing the invocation.
198
CHAPTER 7 CLASSES AND INHERITANCE
By making a small change in the declaration of method Average, you can use the instance invocation form. The change you need to make is to add the keyword this before the type name in the parameter declaration as shown following. Adding the this keyword to the first parameter of the static method of the static class changes it from a regular method of class ExtendMyData into an extension method of class MyData. You can now use both invocation forms.
Must be a static class
|
↓ |
|
|
|
|
||
static class ExtendMyData |
|
|
|
||||
{ Must be public and static |
Keyword and type |
||||||
|
|
|
↓ |
|
|
↓ |
|
public static double Average( this MyData md )
{
...
)
}
The important requirements for an extension method are the following:
•The class in which the extension method is declared must also be declared static.
•The extension method itself must be declared static.
•The extension method must contain as its first parameter type the keyword this, followed by the name of the class it is extending.
Figure 7-22 illustrates the structure of an extension method.
Figure 7-22. The structure of an extension method
199
CHAPTER 7 CLASSES AND INHERITANCE
The following code shows a full program, including class MyData and extension method Average declared in class ExtendMyData. Notice that method Average is invoked exactly as if it were an instance member of MyData! Figure 7-22 illustrates the code. Classes MyData and ExtendMyData together act like the desired class, with three methods.
namespace ExtensionMethods
{
sealed class MyData
{
private double D1, D2, D3;
public MyData(double d1, double d2, double d3) { D1 = d1; D2 = d2; D3 = d3; }
public double Sum() { return D1 + D2 + D3; }
} |
|
|
|
|
|
|
static class ExtendMyData |
Keyword and type |
|||||
{ |
|
|
↓ |
|
||
public static double Average(this MyData md) |
||||||
{ |
↑ |
|
|
|
|
|
|
Declared static |
|
|
|
|
|
} |
return md.Sum() / 3; |
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
class Program |
|
|
|
|
|
|
{ |
|
|
|
|
|
|
static void Main() |
|
|
|
|
|
|
{ |
MyData md = new MyData(3, 4, 5); |
|||||
|
||||||
|
Console.WriteLine("Sum: |
|
{0}", md.Sum()); |
|||
|
Console.WriteLine("Average: {0}", md.Average()); |
|||||
} |
|
|
|
|
↑ |
|
} |
Invoke as an instance member of the class |
}
This code produces the following output:
Sum: 12
Average: 4
200