C#:高级篇学习

委托

C#中的委托 类似于 C/C++函数的指针

委托:表示引用某个方法的引用类型变量,运行时可以更改引用对象

委托可以用于处理事件和回调函数,所有委托类都是从System.Delegate类继承而来的

声明委托

需要定义能够被委托所引用的方法,任意委托可以引用与该委托拥有相同签名的方法

public delegate int MyDelegate(string s);
//可以引用任何一个以字符为参数的方法,且返回值类型为整型
//声明委托的句法规则
delegate

实例化委托

声明委托之后,必须使用new关键字和一个特定的方法来创建一个委托对象。创建时,传递到new语句的参数写法与方法调用相同,但是不带有参数

using System;

delegate int NumberChanger(int n);
namespace Consol_App
{
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }
        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }
        static void Main(string[] args)
        {
            //创建委托实例
            NumberChanger nc1 = new NumberChanger(AddNum);
            NumberChanger nc2 = new NumberChanger(MultNum);

            //使用委托对象调用方法
            nc1(25);
            Console.WriteLine("Value of Num:{0}", getNum());
            nc2(5);
            Console.WriteLine("Value of Num:{0}", getNum());
            Console.ReadKey();
        }
    }
}

委托的多播

委托对象可通过“+”运算符进行合并。一个合并委托可以调用它所合并的两个委托,但只有相同类型的委托可以被合并。

“-”运算符则可用于从合并的委托移除一个委托。

using System;

delegate int NumberChanger(int n);
namespace Consol_App
{
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }
        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }
        static void Main(string[] args)
        {
            //创建委托实例
            NumberChanger nc;
            NumberChanger nc1 = new NumberChanger(AddNum);
            NumberChanger nc2 = new NumberChanger(MultNum);

            nc = nc1;
            nc += nc2;

            //调用多播
            nc(5);  //顺序执行
            Console.WriteLine("Value of Num:{0}", getNum());   //75
            Console.ReadKey();
        }
    }
}

委托的使用

下面的实例演示了委托的作用,示例中的printString委托可用于引用带有一个字符串作为输入的方法, 且不返回数据吧

using System;
using System.IO;

namespace Consol_App
{
    class PrintString
    {
        static FileStream fs;
        static StreamWriter sw;
        //委托声明
        public delegate void printString(string s);

        //该方法打印到控制台
        public static void WriteToScreen(string str)
        {
            Console.WriteLine("The String is: {0}", str);
        }
        public static void WriteToFile(string s)
        {
            fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
            sw = new StreamWriter(fs);
            sw.WriteLine(s);
            sw.Flush();
            sw.Close();
            fs.Close();
        }

        //该方法把委托作为参数,并使用它调用方法
        public static void sendString(printString ps)
        {
            ps("hello world");
        }
        static void Main(string[] args)
        {
            printString ps1 = new printString(WriteToScreen);
            printString ps2 = new printString(WriteToFile);
            sendString(ps1);
            sendString(ps2);
            Console.ReadKey();
        }
    }
}

C#事件


事件指一个用户操作:按键、点击、移动鼠标,也可以是系统生成的通知。

当事件发生时,应用需要对其做出相应 的反应,如中断。

另外,事件也用于内部进程通信

通过事件使用委托

事件生成于类的声明中,通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件,称为发布器

其他接受该事件的类称为订阅器类。事件使用的是发布-订阅模型

发布器是一个定义了事件和委托的对象,此外还定义了事件和委托之间的关联。一个发布器类的对象调用这个事件,同时通知其他的对象。

订阅器是一个接受事件并提供事件处理程序的对象。在发布器类中的委托调用订阅器类中的方法

声明事件

在类中声明一个事件,首先需要声明该事件对应的委托类型

public delegate void BoilerLogHandler(string status)

 使用event关键字声明这个事件

public event BoilerLogHangler BoilerEventLog;

该事件生成时会自动调用一个委托

using System;
 
namespace Consol_App
{
    using System;
    public class EventTest
    {
        private int value;
        public delegate void NumMainpulationHandler();
        public event NumMainpulationHandler ChangeNum;
        protected virtual void OnNumChanged()
        {
            if (ChangeNum != null)
            {
                ChangeNum();
            }
            else
            {
                Console.WriteLine("Event fired!");
            }
        }
        public EventTest(int n)
        {
            SetValue(n);
        }
        public void SetValue(int n)
        {
            if (value != n)
            {
                value = n;
                OnNumChanged();
            }
        }
    }
    public class MainClass
    {
        public static void Main()
        {
            EventTest e = new EventTest(5);
            e.SetValue(7);
            e.SetValue(0);
            e.SetValue(11);
            Console.ReadKey();
        }
    }
}

示例2

该实例为一个简单的应用程序,该程序用于热水锅炉系统故障排除。当维修工程师检查锅炉时,锅炉的温度、压力以及工程师所写 的备注都会自动记录到一个日志文件中

using System;
using System.IO;

namespace Consol_App
{
    class Boiler
    {
        private int temp;
        private int pressure;
        public Boiler(int t, int p)
        {
            temp = t;
            pressure = p;
        }
        public int getTemp()
        {
            return temp;
        }
        public int getPressure()
        {
            return pressure;
        }
    }
    //事件发布器
    class DelegateBoilerEvent
    {
        public delegate void BoilerLogHandler(string status);
        //基于上述委托定于的事件
        public event BoilerLogHandler BoilerEventLog;
        public void LogProgcess() //检验数据
        {
            string remarks = "o . k";
            Boiler b = new Boiler(100, 12);
            int t = b.getTemp();
            int p = b.getPressure();
            if (t > 150 || t < 100 || p < 12 || p > 15)
            {
                remarks = "Need Maintenance";
            }
            OnBoilerEventLog("Logging Info:\n");
            OnBoilerEventLog("Temparature" + t + "\nPressure:" + p);
            OnBoilerEventLog("\nMessage:" + remarks);
        }
        protected void OnBoilerEventLog(string message)
        {
            if (BoilerEventLog != null)
            {
                BoilerEventLog(message);
            }
        }
    }

    //该类保留写入日志文件的条款
    class BoilerInfoLogger
    {
        FileStream fs;
        StreamWriter sw;
        public BoilerInfoLogger(string filename)
        {
            fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
            sw = new StreamWriter(fs);
        }
        public void Logger(string info)
        {
            sw.WriteLine(info);
        }
        public void close()
        {
            sw.Close();
            fs.Close();
        }
    }
    //事件订阅器
    public class RecordBoilerInfo
    {
        static void Logger(string info)
        {
            Console.WriteLine(info);
        } 
        static void Main(string[] args)
        {
            BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");  //文件的对象 c创建文件
            DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();  //事件发布器的实例
            boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger);  //委托
            boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger);
            boilerEvent.LogProgcess();
            Console.ReadLine();
            filelog.close();
        }
    }
}

特性

在运行时传递程序中各种元素(类、方法、结构、枚举、组件)的行为信息的声明性标签。可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息

.Net框架提供了两种类型的特征:预定义特性和自定义特性

列举特性

[attribute(positional_parameters,name_parameter = value ,...)]element

预定义特性

.Net Framework提供了三种预定义的特性:

  • AttributeUsage
  • Conditional
  • Obsolete
AttributeUsage

该特性描述了用户定义的特性类是如何使用的,它规定了某个特性应用的项目类型

[AttributeUsage(validon,AlolowMultiplr = allowmultiple,Inherited = inherited)]
//validon:承载特性的语言元素
//allowmultiple:如果为true,该特性是多用的。默认值是false(单用的)
//inherited:如果为true,该特性可被派生类继承。默认值是false(不可继承)
Conditional

这个预定义特性标记了一个条件方法,其执行依赖于特定的预处理标识符

它会引起方法调用的条件编译,取决于指定的值,比如Debug或Trace。例如,当调试代码时显示变量的值

[Conditional(conditionalSymbol)]
[Conditional("DEBUG")]
using System;
using System.Diagnostics;
class MyClass
{
    [Conditional("DEBUG")]
    //条件编译,可以图通过Conditional指定函数和属性是否编译到最终产品中去
    public static void Message(string msg)
    {
        Console.WriteLine(msg);
    }
}
class Test
{
    static void function1()
    {
        MyClass.Message("In Function 1.");
        function2();
    }
    static void function2()
    {
        MyClass.Message("In Function 2.");
    }
    public static void Main()
    {
        MyClass.Message("In Main function.");
        function1();
        Console.ReadKey();
    }
}
Obsolete

标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素

例如:当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为obsolete(过时的)

[Obsolete(message)]
[Obsolete(message.iserror)]

//message 字符串,描述项目为什么过时的原因以及该替换使用什么
//iserror 布尔值,true:编译器应把该项目的使用当作一个错误
                //false(编译器生成一个警告)
using System;
public class MyClass
{
    [Obsolete("Don't use OldMethod,use NewMethod instead", true)]
    static void OldMethod()
    {
        Console.WriteLine("It is the old method");
    }
    static void NewMethod()
    {
        Console.WriteLine("It is the new method");
    }
    public static void Main()
    {
        OldMethod();
    }
}

编译器会报错

创建自定义的特性

.Net允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关

创建并适应自定义特性包含四个步骤:

  • 声明
  • 构建
  • 在目标程序元素上应用
  • 通过反射访问特性

属性

//为整形类型声明一个叫Age的属性
public int Age
{
    get
    {
        return age;
    }
    set
    {
        age = value;
    }
}

抽象属性

抽象类可拥有抽象属性,这些属性应在派生类中被实现

索引器

element-type this[int index]
{
    //get访问器
    get
    {
        //返回index指定的值
    }
    //set访问器
    set
    {
        //设置index指定的值
    }
}

泛型

泛型允许编写一个可以与任何数据类型协作的类或方法

pubilc class MyGenericArray
{
    public T[] array;
}

  泛型方法

static void Swap(ref T lhs,ref T rhs)
{
    t temp;
    temp = lhs;
    rhs = temp;
}

//调用Swap进行交换
Swap(ref a, ref b);
Swap(ref c, ref d);

   泛型委托

delegate T NumberChanger(T n)

匿名方法

将一段代码块作为委托参数的技术
 

//使用匿名方法创建委托实例
delegate void NumberChanger(int n);
NumberChanger nc = delegate(int x)
{
    Console.WriteLine("Anonymous Method:{0}",x);//匿名方法的主体
}

//使用匿名方法调用委托
nc(10);

不安全代码

unsafe:使用了指针变量的代码----不安全代码

使用了unsafe修饰符时指针的使用

static unsafe void Main(string[] args)
{
    int var = 20;
    int* p = &var;
    Console.WriteLine("Data id:{0}",var);
    Console.WriteLine("Address is:{0}",(int)p);
    Console.ReadKey();
}

//输出
Data is:20
Address is: 99215364

编译不安全的代码

必须切换命令行编译器到指定的/unsafe命令行才能进行不安全代码的编译

例如,编译一个包含不安全代码的名为prog1.cs的程序,需要在命令行中输入如下命令

csc /unsafe prog1.cs

你可能感兴趣的:(c#,学习,java)