C# 入门

作者: 那些年搬过的砖 | 来源:发表于2019-08-02 13:10 被阅读0次

    本文分三个部分
    .NET 体系结构
    根据实际项目理解.NET的分层架构
    C#的一些用法

    一、.NET 体系结构

    1、基本概念

    1.1、专业名词

    .NET Framework:包含CTS(公共类型系统)、CLR(公共语言运行库)
    CIL:Common Intermediate Language 通用中间语言
    JIT:Just-In-Time 编译器
    CLR:Common Language Runtime 公共语言运行库
    FLC:.NET Framework Class Library .NET框架类库

    1.2、首次编译
    C#程序编译为CIL,存储到程序集中

    程序集包括可执行的应用程序文件(exe)和其他应用程序使用的库(dll)。

    1.3、二次编译

    在执行应用程序时,JIT把CIL编译为本机代码


    程序集经过二次编译为本机代码
    1.4、CLR环境运行本机代码
    CLR环境运行本机代码

    2、 .NET体系架构

    ASP.NET:是一种WEB开发框架技术
    .NET:是一个平台
    C#:是一个基于.NET的开发语言


    3、C# 三层架构

    二、根据实际项目理解.NET的分层架构

    1、工程结构

    1.1、设置启动项目
    1.2、启动项目服务器设置
    1.3、页面请求

    ASP.NET中通常将页面和代码分离(前置页面、后置代码),燃气表项目通过上面可以知道,项目启动时会首先加载Default.aspx页面。Default.aspx页面访问时会自动执行后置代码的Page_load方法

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SunMeter.Web.Main.Default"  EnableSessionState="true"%>
    
    protected void Page_Load(object sender, EventArgs e)
     {
         try
         {
             if (Session["username"] == null || Session["username"].ToString() == "")
             {
                 Response.Write("<script  language='javascript' defer>this.parent.parent.location.href='login.aspx';</script>");
                        return;
                  ........
             }
        }
    }
    

    aspx文件是前置代码,cs是对应的后置代码,负责处理aspx中<%%>和runat="server"的内容,由CodeBehind属性绑定,Inherits定义aspx页面所要继承的父类的名称,因为aspx会单独生成一个类,不和后台代码重合。

    上面代码可以看出,访问Default.aspx时会重定向到login.aspx页面,同样的方式会接着执行login.aspx.cs的Page_Load方法。并生成html返回给客户端,所以服务器对aspx的处理就是渲染html的过程。


    aspx请求过程
    1.4、开发环境、测试环境、发布环境

    三、高级C#技术

    1、类型推断

    C#是一种强类型化的语言,每个变量都有固定的类型。从C#3引入了新关键字var,可以代替变量的类型,但是并不是说变量的类型就是var,也不是说变量的类型是可变的,而是说变量的类型采用了隐式化的类型,具体类型依赖编译器来确定。所以var申明的变量,必须同时初始化该变量,否则编译器就无法确认变量的类型.

    语句 是否可编译
    var name = 1;
    var name;

    匿名指定数组时,必须满足以下条件之一
    1:相同的类型
    2:相同的引用类型或空
    3:所有元素的类型都可以隐式转换为一个类型

    语句 是否可编译
    var myArray = new[] { 1, "test", 2 };
    var myArray = new[] { 1, null, 2 };
    var myArray = new int?[] { 1, null, 2 };
    2、匿名类型

    匿名类型是简化编程模型的一种方式,C#编译器会根据要存储的数据自动创建类型
    如下实体对象Model

    public class Student
    {
        public string name {get; set;}
    }
    

    实例化该Model

    Student student = new Student
    {
        name = "东方不败";
    };
    

    如果使用匿名类型

    var student = new 
    {
        name = "东方不败";
    };
    

    注意:匿名属性是只读的,无法修改其属性的值

    1:匿名类使用var关键字
    2:new 后面没有类型名

    3、动态查找

    前面说到C#是一种强类型化的语言,但是从C#4开始,引入“动态变量”的概念,也就是类型可变的变量。

    //定义动态变量
    dynamic dynamicParam;
    
    
        public class Student
        {
            public string say() => "I am a student";
        }
    
        public class Teacher
        {
            public string say() => "I am a teacher";
        }
        class Start
        {
            static dynamic GetPerson(string type)
            {
                if (type == "0")
                {
                    return new Student();
                }
                else
                {
                    return new Teacher();
                }
            }
            static void Main(string[] args)
            {
                while (true)
                {
                    string line = ReadLine();
                    dynamic person = GetPerson(line);
                    WriteLine(person.say());
                }
            }
        }
    

    运行结果


    var 与dynamic比较

    -- 差异
    var 编译器语法糖,编译时自动匹配实际类型
    dynamic 编译后实际是一个Object类型
    4、高级方法参数
    4.1、可选参数值
    public static void print(String text, Boolean b)
    {
                if (b)
                {
                    WriteLine(text);
                }
                else
                {
                    WriteLine("default string print");
                }
    }
    

    一般调用方式

    print("sssss", false);
    print("sssss", true);
    

    如果布尔型参数不是必须的,不想传递时,可以如下改造,类似java的重载

    public static void print(String text, Boolean b)
    {
                if (b)
                {
                    WriteLine(text);
                }
                else
                {
                    WriteLine("default string print");
                }
    }
    public static void print(String text) => print(text, false);
    

    还有一种更简单的方式。

    public static void print(String text, Boolean b=false)
    {
                if (b)
                {
                    WriteLine(text);
                }
                else
                {
                    WriteLine("default string print");
                }
    }
    
    print("sssss", true);
    print("sssss");
    

    还有一种就是通过Optional关键字指定可选参数,但是不能指定默认值

    public static void print(String text, [Optional] Boolean b)
    {
                if (b)
                {
                    WriteLine(text);
                }
                else
                {
                    WriteLine("default string print");
                }
    }
    
    4.2、可选参数的顺序

    1:可选参数必须放在参数列表尾部
    2:有默认值得参数必须放在没有默认值参数的前面

    委托使用的过程
    1:定义委托声明
    2:定义委托变量
    3:委托变量的初始化(实际上就是将函数的引用赋值给委托变量)
    4:使用委托

    5、委托的理解

    1:委托是一种存储函数引用的类型
    2:使用delegate关键字定义委托,同时声明返回类型和参数列表。

    namespace test
    {
        class Program
        {
            //定义一个委托
            delegate double ProcessEvent(double p1, double p2);
    
            //委托变量
            static ProcessEvent process;
    
            static double Add(double p1, double p2) => p1 + p2;
            static double Subtract(double p1, double p2) => p1 - p2;
    
            static void Main(string[] args)
            {
                Console.WriteLine("Enter 2 numbers:");
                string line = ReadLine();
                int pos = line.IndexOf(',');
                double p1 = ToDouble(line.Substring(0,pos));
                double p2 = ToDouble(line.Substring(pos+1, line.Length - pos - 1));
                Console.WriteLine("Enter type:");
                line = Console.ReadLine();
                if(line == "a")
                {
                    //将函数引用赋给委托变量(初始化与委托有相同返回值和参数列表的函数引用.就可以通过该委托变量调用这个函数)
                    process += new ProcessEvent(Add);
                }
                else
                {
                    process += new ProcessEvent(Subtract);
                }
                //使用该委托调用选择的处理函数
                WriteLine(process(p1, p2));
            }
        }
    }
    

    6、事件的处理

    事件是.NET中最常用的OOP技术。事件类似异常,但是没有try-catch。事件的引发和订阅实际上就是一种委托,事件的处理方法的返回值和参数由委托指定。
    C#中事件的编程分为两部分:事件触发、事件的订阅(处理)。而委托就是事件发生方和处理方之间的一个桥梁。


        //申明委托类型
        public delegate void KeyboardHandler(String input);
    
        //事件发布者
        class Sender
        {
            //事件定义关键字  委托类型  事件名称
            public event KeyboardHandler Keydown;
            public void Run()
            {
                while (true)
                {
                    string line = ReadLine();
                    //触发事件
                    Keydown(line);
                }
            }
        }
    
        //事件订阅者(多个订阅者可同时订阅一个事件)
        class Process
        {
            public Process(Sender sender)
            {
                //事件的注册(+=将处理程序挂载到事件,-=表示事件卸载处理程序,可添加多个处理函数)
                sender.Keydown += new KeyboardHandler(OnKeyDown);
            }
            //真正的事件处理函数
            private void OnKeyDown(String input)
            {
                WriteLine("Ascii code of Char " + input + " is:"  + (short)(input[0]));
            }
        }
    
        class Start
        {
            static void Main(string[] args)
            { 
                //实例化事件发送器
                Sender sender = new Sender();
                //实例化事件处理器
                Process process = new Process(sender);
                //启动运行
                sender.Run();
            }
        }
    

    事件注册的具体格式如下:
    sender.Keydown += new KeyboardHandler(OnKeyDown);
    事件 += new 委托类型(方法标识符);
    即如果触发sender对象的Keydown事件,就调用OnKeyDown方法来处理,传递给OnKeyDown的参数取决于委托类型KeyboardHandler定义的参数。
    事件的注册还有一种更简洁方式
    sender.Keydown += OnKeyDown;
    运行结果

    7、Lambda表达式

    (未类型化的参数列表)=>C#语句
    =>是运算符
    上面事件的注册可以用匿名方法或Lambda表达式来改造

    public Process(Sender sender)
    {
          //事件的注册
          sender.Keydown += new KeyboardHandler(OnKeyDown);
    }
    //真正的事件处理函数
    private void OnKeyDown(String input)
    {
          WriteLine("Ascii code of Char " + input + " is:"  + (short)(input[0]));
    }
    

    用匿名方法改造

    public Process(Sender sender)
    {
         sender.Keydown += delegate(string input) 
         {
              WriteLine("Ascii code of Char " + input + " is:" + (short)(input[0]));
         };
    }
    

    可以看出OnKeyDown处理函数通过匿名方法形式,对其他应用程序来说是隐藏的,也就是说无法重用这个事件处理函数。

    需要注意的一点,匿名方法和委托类型的定义都用到了delegate关键字。

    Lambda表达式用于匿名方法

    public Process(Sender sender)
    {
          //lambda表达式注册及处理事件
          sender.Keydown += (input)=> WriteLine("Ascii code of Char " + input + " is:" + (short)(input[0]));
    }
    

    实际上,编译器在提取Lambda表达式时,会创建一个匿名方法。Lambda表达式的参数实际就是用到了前面所说的类型推断来确定参数的类型,也可以指定参数类型。Lambda表达式也可以执行多个语句,可以有返回值。

    (param1...paramN) =>
    {
        //语句1
        //语句2
        return value;
    }
    

    附录一

    快捷键
    F5:跳转到下一断点
    F10:单步调试
    “Ctrl + -” : 返回到上一个光标处
    “Ctrl + Shift + - ”:前进到下一个光标位置

    ····

    相关文章

      网友评论

          本文标题:C# 入门

          本文链接:https://www.haomeiwen.com/subject/augarctx.html