C#基础笔记

本文档详细介绍了C#编程的基础知识,包括命名规范、初始化、字段使用、属性和索引器、委托、事件、异常处理、泛型、集合与列表等。强调了在多线程环境下使用volatile关键字的重要性,以及如何通过属性进行值验证和自动实现。还讨论了扩展方法、继承和接口的使用,以及避免使用析构函数和如何利用处置模式来管理资源。
摘要由CSDN通过智能技术生成

切套类的使用

There is no strong convention on using nested classes in C#, but I tend to use them when I need to
define a class that is used solely by the outer class or to provide implementations of interfaces.

命名规范

The convention for naming C# fields is to use camel case, such as itemInStock;
For pbulic & static fields, the convention is to use Pascal case, such as ItemInStock;
however, there is an even stronger convention against using public or static fields, preferring properties instead.

Once again, though, let me say that you should use a public static property in preference to a public static field.

初始化

定义时初始化:

class Product {
   
int itemsInStock = 210;
Supplier productSupplier = new Supplier();
}

或者用constructor初始化
If you define but don’t initialize a field, the field value will be the default value for the field type,
such as 0 for numeric types, null for reference types, and so on.

只读field

C# supports two slightly different read-only fields, which are available through the const and readonly keywords.

class Product {
   
public const int UnitsPerCrate = 10;
}

A readonly field can be assigned a value when it is defined or in a class constructor but is otherwise immutable.
Allowing a readonly field to be modified during object construction is a useful feature. It means that you can create a per-instance constant value, which depends on the way in which your object was constructed.

隐藏基类Fields

If you want to define a field with the same name as a field in your base class, you can do so by using the new keyword.

class Product {
    protected int myField;
}
class DerivedProduct : Product {
    private new string myField;
    public int MyIntProperty {
        get { return base.myField; }
        set { base.myField = value; }
    }
    public string MyStringProperty {
        get { return this.myField; }
        set { this.myField = value; }
    }
}

In short, if an object created from a class that hides a base field is upcast to a base type, then the hidden field will be the one that is accessed by other classes. If the same object is referred to by its declared type, then the new field is access.

using System;
class BaseProduct {
   
    public string myField = "Base Value";
}
class DerivedProduct : BaseProduct {
   
    public new string myField = "Derived Value";
}
class Listing_17 {
   
    static void Main(string[] args) {
        // create an object from the derived class
        DerivedProduct derivedObject = new DerivedProduct();
        // upcast the objet to the base type
        BaseProduct upcastObject = derivedObject;
        // print out the field values
        Console.WriteLine("Derived Field: {0}", derivedObject.myField);//output: Derived Field: Derived Value
        Console.WriteLine("Base Field: {0}", upcastObject.myField);//output: Base Field: Base Value
        // wait for input before exiting
        System.Console.WriteLine("Press enter to finish");
        System.Console.ReadLine();
    }
}

You can see that upcasting an object has the effect of “unhiding” the field in the base class. When hiding fields, you must be confident of how objects created from your class are going to be treated, since upcasting changes the way that they behave.

使用volatile关键字

The volatile modifier is applied to fields that may be accessed by multiple threads without the use of the lock keyword or other synchronization mechanisms. This keyword prevents some compiler optimizations that assume access by a single thread. You can learn more about synchronization when we explore parallel and multithreaded programming techniques later in the book.

Properties/Indexers/Fields

Properties are class members that allow you to expose a characteristic of an object.
Indexers are members that allow you to treat a class as though it were an array, using the same index syntax ([])
Properties and indexers both allow you to control access to the fields in your class and give you more control than exposing a field directly. As you’ll see when we get into the detail, properties and indexers are very flexible; in fact, they are so rich that they can sometimes blur the distinction between fields and methods.

Creating a Field-Backed Property

The standard way of using a property is to use it as a mediator to a field, typically to ensure that other classes cannot set the field to a value that would create an exceptional state for your class.

Using a Property to Validate Field Values

class Product {
    private int itemsInStock;
    public double PricePerItem;
    public int ItemsInStock{
    get{
  return itemsInStock;}
    set{
        if(value >=0){itemsInStock = value;}
        else{
  throw new ArgumentOutOfRangeException();}
    }
}

Creating an Automatically Implemented Property

There are no bodies for the accessors in an automatically implemented property. You just use the get and set keywords, followed by a semicolon. Most importantly, there is no field either.

class Product {
   
    public int ItemsInStock {
  get;set;}
}

Creating an Asymmetric(不对称的) Property

You don’t have to implement both accessors in a property. If you omit the set accessor, you create a read-only property, and if you omit the get accessor, you create a write-only property.

Creating a Computed Property

Properties are very flexible and need not be used just to mediate access to fields. They can also be used to access values that are computed on the fly.

class Product {
    public int ItemsInStock { get; set; }
    public double PricePerItem { get; set; }
    public double TotalValueOfStock {
        get { return ItemsInStock * PricePerItem;}
    }
}

there are two automatically implemented properties, ItemsInStock and PricePerItem, and a read-only computed property called TotalValueOfStock, which uses the other two properties to return a result.

Mapping a Property Type to a Field Type

You can use a property of one type to mediate access to a field of a different type by implementing the conversion between types in the accessors.
such as maps a string property to a double field.

Using Access Modifiers to the get and set accessors

class Product {
    public int ItemsInStock {
        private get;
        set;
    }
}

Using Other Modifiers: virtual, overrides, abstract, sealed, static…

Indexers allow you to expose the data contained by an object using array notation([]).

class Product{
    private string[] productNames = new string[]{
  "orange", "apple", "pear", "banana", "cherry"};
    public string this[int index]{
        get{
  return productNames[index];}
        set{productNames[index] = value;}
    }
}
Produce p = new Product();
p[0] = "test";

You can see from the Main method in the previous class that using an indexer is just like using an array. The listing demonstrates the most common use of indexers, known as the collection-backed indexer.

Extension Methods

Extension methods allow you to seemingly add methods to classes without modifying them. I say
seemingly, because extension methods are a clever feature that makes it appear as though a method has been added to a class when it hasn’t really.

Extension methods are most useful when you don’t have access to the source code of the class you want to work with.

Extension methods must be static and can exist only inside a static class.
Unlike partial methods, the name of the class that contains the extension methods doesn’t matter.
The first parameter in an extension method must have the this modifier, and the type of the
parameter is the type on which the extension method will operate.

class Person {
  public string Name { get; set; }
  public int Age { get; set; }
  public string City { get; set; }
  public Person(string name, int age, string city) {
    Name = name; Age = age; City = city;
  }
}
static class ExtensionMethods {
  public static void PrintInformation(this Person p) {
    Console.WriteLine("--- Person ---");
    Console.WriteLine("Name: {0}", p.Name);
    Console.WriteLine("Age: {0}", p.Age);
    Console.WriteLine("City: {0}", p.City);
  }
}
Person p = new Person("Adam Freeman", 38, "London");
p.PrintInformation();// call the extension method

You don’t need to provide a value for the first parameter in an extension method (the one modified
with this). It is implicit from the object that you have called the method on. Inside an extension method,
you can refer to this parameter to access the members of the object. Extension methods don’t have any
special access to the members of an object, so you can only use those members to which you have access. This will typically be those that are modified using the public or internal keywords.
This is what I meant when I said that extension methods seem to extend classes. In fact, we have
defined an entirely method in an entirely separate class, but the magic of the C# compiler allows us to
use it as though it were part of the original Person class.

Delegate

A delegate is a special C# type that represents a method signature.

**AccessModifier delegate ResultType DelegateName(parameters);
public delegate int PerformCalc(int x, int y);**

DelegateName is equivalent to the class name. It is important to bear in mind that when we define a new delegate, we are defining a new type.
You can define a new delegate type in the same places as you can create a new class - in a namespace, class, or struct.
Once we have defined a new delegate type, we can create an instance of it and initialize it with a value.
You can also use generic types with delegates. If we wanted a generic version of the delegate type:

public delegate T PerformCalc<T>(T x, T y);
class Calculator {
    PerformCalc<int> perfCalc;

Using Delegates for Callbacks

You can use delegates to create callbacks, where one object is notified when something of interest happens in another object.

delegate void NotifyCalculation(int x, int y, int result);
class Calculator {
  NotifyCalculation calcListener;
  public Calculator(NotifyCalculation listener) {
    calcListener = listener;
  }
  public int CalculateProduct(int num1, int num2) {
    int result = num1 * num2;
    calcListener(num1, num2, result);// notify the delegate that we have performed a calc
    return result;
  }
}

class CalculationListener {
  public static void CalculationPrinter(int x, int y, int result) {
    Console.WriteLine("Calculation Notification: {0} x {1} = {2}", x, y, result);
  }
}

Calculator calc = new Calculator(CalculationListener.CalculationPrinter);
calc.CalculateProduct(10, 20);//Calculation Notification: 10 x 20 = 200
calc.CalculateProduct(2, 3);//Calculation Notification: 2 x 3 = 6

When performing callbacks, you will often need to cater for multiple listeners, rather than the single
listener. The delegate type uses custom + and – operators that let you combine several method references together into a single delegate and invoke them in one go, known as
multicasting.

delegate void NotifyCalculation(int x, int y, int result);
class Calculator {
    NotifyCalculation calcListener;
    public void AddListener(NotifyCalculation listener) {
        calcListener += listener;
    }
    public void RemoveListener(NotifyCalculation listener) {
        calcListener -= listener;
    }
    public int CalculateProduct(int num1, int num2) {
        int result = num1 * num2;
        calcListener(num1, num2, result);// notify the delegate that we have performed a calc
        return result;
    }
}

class CalculationListener {
    private string idString;
    public CalculationListener(string id) {
        idString = id;
    }
    public void CalculationPrinter(int x, int y, int result) {
        Console.WriteLine("{0}: Notification: {1} x {2} = {3}", idString, x, y, result);
    }
}
class AlternateListener {
    public static void CalculationCallback(int x, int y, int result) {
        Console.WriteLine("Callback: {0} x {1} = {2}", x, y, result);
    }
}
// create a new Calculator
Calculator calc = new Calculator();

// create and add listeners
calc.AddListener(new CalculationListener("List1").CalculationPrinter);
calc.AddListener(new CalculationListener("List2").CalculationPrinter);
calc.AddListener(AlternateListener.CalculationCallback);

// perform a calculation
calc.CalculateProduct(10, 20);

// remove a listener
calc.RemoveListener(AlternateListener.CalculationCallback);

// perform a calculation
calc.CalculateProduct(10, 30);

Interrogating Delegates代理查询

The base type for all delegates is System.Delegate, and we can use the members of this class to find out
which methods a delegate will invoke on our behalf.

delegate int PerformCalc(int x, int y);
class Calculator {
    public int CalculateSum(int x, int y) {
        return x + y;
    }
}
class AlternateCalculator {
    public int CalculateProduct(int x, int y) {
        return x * y;
    }
}
// create a delegate variable
PerformCalc del = new Calculator().CalculateSum;
// combine with another method
del += new AlternateCalculator().CalculateProduct;
// Interrogate the delegate
Delegate[] inlist = del.GetInvocationList();
foreach (Delegate d in inlist) {
    Console.WriteLine("Target: {0}", d.Target);
    Console.WriteLine("Method: {0}", d.Method);
}

Using Events

Events are specialized delegates designed to simplify the callback model.
There can be a problem when you use a delegate type as a field, where one object interferes with another.

1). The first step in defining an event is to derive a class from the System.EventArgs class, which contains
properties and fields to contain any custom data that you want to pass when you invoke the event
delegate; the name of this class should end with EventArgs.

You can include any fields and properties that you need to express information about your event,
but you should make sure that the fields cannot be modified. This is because the same EventArgs object
will be passed to each of the subscribers of your event, and a poorly or maliciously coded recipient could
change the information that subsequent listeners receive.

class CalculationEventArgs : EventArgs {
    private int x, y, result;
    public CalculationEventArgs(int num1, int num2, int resultVal) {
        x = num1;
        y = num2;
        result = resultVal;
    }
    public int X {
        get { return x; }
    }
    public int Y {
        get { return y; }
    }
    public int Result {
        get { return result; }
    }
}

2)The next step is to define an event in your class. To define the event using the generic EventHandler delegate.
The convention is that the name of the event should end with the word Event.
The convention dictates that you put the code to invoke your event delegate in a method whose name starts with On concatenated with the event name, less the word event.
So, for example, since we have defined an event called CalculationPerformedEvent, the method would be called OnCalculationPerformed.
This method should make a copy of the event to avoid a race condition and ensure that the event has subscribers by ensuring that the event field is not null.

class Calculator {
    public event EventHandler<CalculationEventArgs> CalculationPerformedEvent;
    public int CalculateProduct(int num1, int num2) {
        int result = num1 * num2;
        OnCalculationPerformed(new CalculationEventArgs(num1, num2, result));// publ
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值