一、C#中value
上下文关键字 value 用在普通属性声明的 set 访问器中。 此关键字类似于方法的输入参数。可以理解为和this指针,是系统自动提供的一个变量。
C#属性:它提供灵活的机制来读取、编写或计算某个私有字段的值。 可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class TimePeriod { private double seconds; public double Hours { get { return seconds / 3600; } set { seconds = value * 3600; } } } class Program { static void Main(string[] args) { TimePeriod t = new TimePeriod(); // Assigning the Hours property causes the 'set' accessor to be called. t.Hours = 24; // Evaluating the Hours property causes the 'get' accessor to be called. System.Console.WriteLine("Time in hours: " + t.Hours); } } |
TimePeriod 类存储一个时间段。 在内部,类以秒为单位存储时间,但Console Application使用名为 Hours 的属性能够以小时为单位指定时间。 Hours 属性的访问器执行小时和秒之间的转换。
1 2 3 4 |
1、属性使类能够以一种公开的方法获取和设置值,同时隐藏实现或验证代码。 2、get属性访问器用于返回属性值,而 set访问器用于分配新值。 这些访问器可以有不同的访问级别。 3、value关键字用于定义由 set 取值函数分配的值。 |
二、yield return
在语句中使用 yield 关键字,则意味着它在其中出现的方法、运算符或 get 访问器是迭代器。 通过使用 yield 定义迭代器,可在实现自定义集合类型的 IEnumerable 和 IEnumerator 模式时无需其他显式类。
IEnumerator <T>支持在泛型集合上进行简单迭代。
1 2 3 4 5 6 |
var testList = TestInfo.GenerateTestSequence( testSelectorTreeView1.CheckedTests,//在treeview中选中的结点数 hardware, testFilterControl1.Cdf, testFilterControl1.SelectedBusTypes ); |
调用以下语句, 对迭代器函数的每个调用将继续到 yield return 语句的下一次执行。
通过 foreach 语句来使用迭代器方法。 foreach 循环的每次迭代都会调用迭代器方法。 //迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前代码中的位置。 //当下次调用迭代器函数时执行从该位置重新启动。
1 2 3 4 5 6 7 |
public static IEnumerable<TestInfo> GenerateTestSequence(IEnumerable<TestParamPair> testAndParams, ITester hardware, ICdf cdf, IEnumerable<BusType> busTypes){ foreach (var test in testAndParams){ foreach (var busType in busTypes.Intersect(test.Metadata.SupportedBusses)){ yield return new TestInfo(test.Metadata, hardware, cdf, busType, test.Param, test.ParamIndex); } } } |
foreach 语句体的每个迭代创建迭代器函数。迭代器方法和 get 访问器迭代器的声明必须满足以下要求:
1)返回类型必须为 IEnumerable、IEnumerable、IEnumerator 或 IEnumerator。
2)该声明不能有任何 ref 或out。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public sealed class TestParamPair{ public Metadata Metadata { get; private set; } public IParamsPointer Param { get; private set; } public int ParamIndex { get; private set; } public TestParamPair(Metadata metadata, IParamsPointer paramToRun, int paramIndex){ if (!metadata.ParamsPointers.Contains(paramToRun)){ throw new ArgumentException("Invalid Param for this test"); } this.Metadata = metadata; this.Param = paramToRun; this.ParamIndex = paramIndex; } } |
简单地说,当希望获取一个IEnumerable类型的集合,而不想把数据一次性加载到内存,就可以考虑使用yield return实现"按需供给"。
第一种方法,是把结果集全部加载到内存中再遍历;第二种方法,客户端每调用一次,yield return就返回一个值给客户端,是"按需供给"。
使用yield return为什么能保证每次循环遍历的时候从前一次停止的地方开始执行呢?
--因为,编译器会生成一个状态机来维护迭代器的状态。
三、IDisposable模式
对于垃圾回收而言,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public interface IDisposable { void Dispose(); } public interface ITester : IDisposable { DeviceType DutCategory { get; } IntPtr NativePtr { get; } RtlVersion VersionInfo { get; } int VBusDrive { set; } BusType Connect(BusType connectionType); void Reset(); void SubscribeLogger(TestController.LogFunc loggingFunction); } public interface ISourceTester : ITester { bool IsAvLinkStable(); ITmdsStream CapturePhyData(); } |
在生成Form的过程中,会有这样的代码
1 2 3 4 5 6 7 8 9 |
protected override void Dispose(bool disposing) { if (disposing && (components != null)) { //释放那些实现IDisposable接口的托管对象 components.Dispose(); } base.Dispose(disposing); } |
Dispose()方法允许抛出异常吗?答案是否定的。如果Dispose()方法有抛出异常的可能,那就需要使用try/catch来手动捕获。
四、C# Interface
接口只包含方法、属性、事件或索引器的签名(但是不可以包含字段),属性和事件在编译的时候编译器会自动将其转化成方法。
实现接口的类(class)或结构(struct)必须实现接口定义中指定的接口成员。 在下面的示例,继承自类ISampleInterface的类Program 必须实现一个不具有参数并返回 void 的名为 SampleMethod 的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//接口可以是命名空间或类的成员,并且可以包含下列成员的签名 interface ISampleInterface{ void SampleMethod(); } class ISample:ISampleInterface{ //注意这里的语法描述,SampleMethod()对类ISample的引用是隐藏的 void ISampleInterface.SampleMethod(){ <em>Console</em>.<em>WriteLine</em>(1); } class Program{ static void Main(string[] args){ ISampleInterface obj = new ISample(); obj.SampleMethod(); } } |
//ISample obj = new ISample();//对象obj的列表中没有方法SampleMethod()。
//ISampleInterface obj = new ISample();
如果一个类实现了一个接口中的方法M,则M应该声明为public,否则编译无法通过。但是如何让类的引用无法使用接口中的方法M呢?
如何访问到SampleMethod()呢?通过接口类型的引用访问即可。
注意:接口是为不相关的类提供通用的功能的,像上面,如果SampleMethod()对类ISample的引用是隐藏的,完全可以将SampleMethod()这样的方法脱离出来再定义一个接口。所以照这样说,上面那种做法是没有必要的。
一个接口可从一个或多个基接口继承。
当基类型列表包含基类和接口时,基类必须是列表中的第一项。
实现接口的类可以显式实现该接口的成员。 显式实现的成员不能通过类实例访问,而只能通过接口实例访问!!!
Example:接口包含属性声明,类包含实现。实现IPoint的类的任何实例都具有整数属性x和y.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
namespace InteropTypes{ public interface ICdf{ bool IsValid { get; } void Validate(); DeviceType DeviceType{ get;} ICdfPtr Ptr { get; } string OriginalFilePath { get; } TesterOptions TesterOptions { get; } } } class ICdf; namespace TestInterop{ /// ///A C++/CLI wrapper for the CDF object. ///This object will let us cross managed/unmanaged boundries with exceptions, ///so we can get more diagnostic information if a CDF fails to parse. public ref class IManagedCdf : public InteropTypes::ICdf{ private: ::ICdf& _cdfRef; <em>System</em>::<em>String</em>^ _originalPath; bool _disposed; IManagedCdf(::ICdf& unmanagedCdf, <em>System</em>::<em>String</em>^ originalPath); protected: !IManagedCdf(); //GC Finalizer void destroy(bool disposing); public: ~IManagedCdf(); //Deterministic destructor (IDisposable) // Property implementation: property bool IsValid{ virtual bool get(); } property InteropTypes::DeviceType DeviceType{ virtual InteropTypes::DeviceType get(); } property InteropTypes::ICdfPtr Ptr{ virtual InteropTypes::ICdfPtr get(); } property <em>System</em>::<em>String</em>^ OriginalFilePath{ virtual <em>System</em>::<em>String</em>^ get(); } property InteropTypes::TesterOptions TesterOptions{ virtual InteropTypes::TesterOptions get(); } /// /// Validates the CDF and throws a Format Exception if there is a problem detected /// Validate provides a mechanism to validate the CDF and also return diagnostic information /// if an error is detected. virtual void Validate(); static IManagedCdf^ ParseCdf(<em>System</em>::<em>String</em>^ path); }; } |
五、C# 接口和 抽象类(abstract class)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public abstract class Metadata : IComparable, IComparable<Metadata> { public abstract string Name { get; } public abstract string Description { get; } public abstract IEnumerable<IParamsPointer> ParamsPointers { get; } public abstract int Iterations { get; } public abstract IEnumerable<BusType> SupportedBusses { get; } public abstract IEnumerable<DeviceType> SupportedDevices { get; } public abstract AutomationType Automation { get; } public abstract TestResult TestDelegate(IParamsPointer param, ITester device, TestController controller, BusType busType); public override string ToString() { } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//通过CLI继承C#的抽象类 public ref class Metadata : public InteropTypes::Metadata { private: //::TestMetadata* _metadata; TestFunc _testFunc; System::String^ _name; System::String^ _description; InteropTypes::AutomationType _automation; int _iterations; const ::ITestParams* const * _params; InteropTypes::DeviceTypeFlags _deviceCategories; InteropTypes::BusTypeFlags _supportedBusTypes; Metadata(::TestMetadata nativeMetadata); public: static System::Collections::Generic::IEnumerable<InteropTypes::Metadata^>^ GetMetadata(); static System::Collections::Generic::IEnumerable<InteropTypes::Metadata^>^ GetMetadata(::InteropTypes::TestCategory categoryFlags); property System::String^ Name{ virtual System::String^ get() override;//virtual 和override } property System::String^ Description{ virtual System::String^ get() override; } property InteropTypes::AutomationType Automation{ virtual InteropTypes::AutomationType get() override; } property int Iterations{ virtual int get() override; } }; |
在Treeview中怎么将结点和Metadata联系起来呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public Metadata SelectedTest{ get{ if (TestSelected){ return (Metadata)this.selectedParent.Tag;} else{ return default(Metadata);} } } private void testSelectorTreeView_SelectedTestChanged(object sender, EventArgs e) { if (testSelectorTreeView.TestSelected){ var metadata = testSelectorTreeView1.SelectedTest; ... } } |
1)抽象类是否可继承接口?
抽象类里面可以包括抽象方法和非抽象方法,而接口只是定义了方法的原型,没有方法的具体定义。从这方面来分析,抽象类是可以继承接口的。
2)接口可以继承抽象类么?
接口只定义成员方法的原型,并不包括成员方法的实现。抽象类里面可能有实现的方法。如果说,接口可以继承抽象类,那这就和“接口只声明成员方法的原型,而没有具体方法的定义”相违背。所以说,接口不能继承抽象类。
抽象类继承接口后,可以实现接口中的成员方法,也可以不实现。如果不实现接口中成员方法,只需将该方法声明为abstract类型就可以了。
1 2 3 4 5 6 |
interface ITemp{ void IM(); } abstract class CTemp : ITemp{ public abstract void IM(); } |
3)(抽象)类继承类和接口的顺序是如何的?
1 2 3 4 5 6 |
class A{ } interface IA{ } interface IB{ } |
基类A必须在任何接口之前,不然编译器会报错。
1 2 |
class B : A, IA, IB{ } |
解释:针对上面的例子,假设类A中有一个方法M,接口IA中同时声明了一个方法M。如果类B是按照IA, IB, A这样的顺序继承,则在B类型执行内存分配时,B对象的方法表里面首先加载接口IA中的方法的实现,之后是IB的,再之后是A的。因为A中也有方法M,所以这样的顺序是不是会认为M是IA中成员M的实现呢?如果类B是按照A, IA, IB这样的顺序继承,就不存在这样的情况了。
4)接口中不能再声明类型。例如接口中不能再定义类,不能再声明接口等等,但是类里面可以再定义类,再声明接口。
1 2 3 4 |
class N{ interface IU { } class CU { } } |
五、abstract、override
abstract:
修饰类名为抽象类,修饰方法为抽象方法。如果一个类为抽象类,则这个类智能是其他某个类的基类。抽象方法在抽象类中没有函数体。抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽象方法。
抽象类有如下特征:
抽象类不能实例化
抽象类的派生类必须实现所有抽象方法
抽象类中的抽象方法是没有方法体的,继承其的子类必须实现抽象类的抽象方法
抽象方法:
抽象方法是隐式的虚方法
只允许在抽象类中使用抽象方法声明
抽象方法在抽象类中没有方法体
在抽象方法声明中,不能使用static或者virtual修饰符
override:
override关键字提供派生类对基类方法的新的实现,重写的基类方法必须和基类的方法有着相同的签名(函数名、返回值、参数列表相同)。
override关键字不可以重写基类非virtual修饰的方法和static修饰的静态方法。
派生类的override方法和基类的virtual方法必须有相同的访问权限。不能用修饰符new、static、virtual或者abstract修饰override方法。
派生类的override方法重写的基类方法必须是virtual、abstract或者override的。
virtual:
virtual 关键字允许在派生类中重写这些对象。默认情况下,方法是非虚拟的,不可以重写非虚方法,virtual关键字不可以与static、abstract、private、override一起使用。virtual关键字又是和override紧密不可分的,如果要实现virtual方法就必须要使用override或new关键字(上文已经指出new和override产生的机理不同)。
sealed:
1 |
public sealed class TestSequence : IEnumerable<TestInfo> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class X{ protected virtual void F() { Console.WriteLine("X.F"); } protected virtual void F2() { Console.WriteLine("X.F2"); } } class Y : X{ sealed protected override void F() { Console.WriteLine("Y.F"); } protected override void F2() { Console.WriteLine("X.F3"); } } class Z : Y{ // Attempting to override F causes compiler error CS0239. // protected override void F() { Console.WriteLine("C.F"); }//sealed修饰的方法是不允许继承的 // Overriding F2 is allowed. protected override void F2() { Console.WriteLine("Z.F2"); } } |