C#基础

架构技术交流 2019-10-28

下文图片出自C#与.NET+4高级程序设计+第5版与百度

.NET理解为一个运行库环境和一个全面的基础类库。

.NET三个关键实体(构造块):CLRCTSCLS

公共语言运行库层为CLR 。功能:定位加载和管理.NET类型。也负责底层的工作如内存管理,处理线程等等。

公共类型系统:CTS 。 描述了运行库所支持所有可能的数据类型和编程结构。

公共语言规范:CLS。定义所有.NET都支持的公用类型和编程结构的子集。

Reflector查看程序集,反编译。

.Net

调用JS函数<script language=”javascript” type=”text/javascript”>   </script>

表单数据传输有两种方式:POST GET

POST方式,表单数据对于外部世界来说不直接可见,没有字符长度限制

GET方式,表单数据会以&分隔。字符受限。

根据最初的HTTP请求创建了程序集,它就将被所有后续的请求重用,不必再次编译。同一页面的后续请求会快很多。

调试跟踪ASP.NET页面 通过将<%@page%>指令中的Trace特性设置为true来启用跟踪支持。

通过Trace.Write()记录日志。

Javascript运行机制

JavaScript一大特点是单线程,同一时间只能做一件事情。

JavaScript将任务分为两种,一种是同步任务,一种是异步任务。

同步:排队执行任务

异步:通过任务队列通知主线程,某个异步可以执行了才会进入主线程执行。

异步运行机制:所有的任务都在主线程上执行,形成一个执行栈。主线程之外存在一个“任务队列”(异步任务产出了结果,就在任务队列中放入一个事件),执行栈中的任务执行完毕就从任务队列中调取任务,看里面有哪些事件,把异步任务进行执行栈,开始执行。

定时器:setTimeoutsetInterval 

setTimeout(function() {  {console.log(i);  }, 1000);第二参数表示每隔多少毫秒执行。

三层架构

三层架构分为UI(表现层),BLL(业务逻辑层) DAL(数据访问层)

 C#基础

使用三层架构的目的是解耦。 任何一层发生变化都不会影响到另外一层。结构清晰,可维护性高,有利于同步开发。

如何获取web.config<connecttionString>的连接字符串

如果name=conString

那么通过代码string connectionString;
connectionString = ConfigurationSettings.AppSettings["conString"];可以获取到conString里面的字符串内容。

今天开会提到的存储过程去查询了一下

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。

存储过程就类似是一个方法并可能带有参数,可以调用。

C#+符号被编译器处理为String.Concat()调用。

字符串是不可变的。

String类型的方法其实返回了一个按修改格式的新字符串对象。

在调用了大写之后,String字符串依旧没有变换。

静态方法可以直接被调用,无需创建类的实例。

成员重载:当定义一组名字相同的成员(它们的参数数量或者类型不同时),这样的成员被叫做重载。 

枚举

在创建系统的时候,创建一组符号名来对应已知的数字值会很方便(符号代替数字)。 例如:

Enum EmpType

{

Manager,//默认情况从0开始, 也可以自己赋值 但也不可以不连续

Grunt,//1

Contractor,//2

VicePresident,//3

}

使用枚举

在上面的枚举中,必须以枚举名(EmpType)来设置值。

设置Grunt 

根据枚举名 EmpType  emp=EmpType.Gruntemp的值就是Grunt

 五大数据类型:类 结构体 枚举 接口 委托

值类型(放在栈,基类是ValueType,分配一个值)和引用类型(放在堆,基类除了ValueType都可以。指向一个内存位置)

值类型在越出定义的作用域时消失,引用类型当堆被垃圾回收时。

值类型赋值给另一个的时候,就是对字段成员进行复制。

引用类型是在内存中重定向引用变量的指向。

C#中定义的值类型包括原类型(SbyteByteShortUshortIntUintLongUlongCharFloatDoubleBoolDecimal)、枚举(enum)、结构(struct

引用类型包括:对象、类、数组、接口、委托、字符串(String)等。

Refout有什么区别?

Out输出参数不需要初始化,Ref引用参数必须初始化

重载(方法名相同,返回类型和参数不同)和重写的区别?

Ref返回多个值。

P2指向的P1的内存。 当P1改变 P2也一样改变。

C#可空类型 null用来建立一个空的对象引用,值类型不能被赋空。

??操作符。 获得的值实际上是null时,我们可以用??给一个可空类型赋值。

默认构造函数不会接受任何参数,例如

Public person(){

Weight = 10

Height=10

}

充当初始化的功能。

构造函数也可以自定义

Public person(int wei)

{

 Weight=wei;

}

没有赋值就是默认值0

总结:构造函数的不同就是参数个数和类型的不同。构造函数没有返回类型 每个class类都有默认的构造函数,但是一旦定义了自定义构造函数,默认的会自动移除。

This关键字

解决传入参数名字与数据类型相同时产生作用域歧义。

 C#基础

当变量名与成员变量类型名字相同时,作用域会指向变量而不是字段

 C#基础

加入this 消除这种歧义。

This关键字还有另一个用法。例如:

对构造函数某个参数需要做卡关,验证。

Class Motocycle{

Int goodspeed

Int drivername

//每次调用构造函数就调用这个方法

Public Motocycle(int speed)

{

Setgoodspeed(speed);

}

Public void Setgoodspeed(int speed)

{

  If(speed>10)

{

speed= 10;

}

 Goodspeed=speed;}}

使用This关键词可以串联构造函数:结构如下

 C#基础

静态static 

静态字段是所有对象共享的,如果需要共享数据可以使用静态成员。

关于静态构造函数

它不可以被重载,静态构造函数只会执行一次,执行优先级最高。

静态类

 C#基础

使用private关键字将默认构造函数定义私有防止创建实例。

使得对象在内存中只存在一个 可参考代码。

public class SimpLeMath
    {
           private SimpLeMath()
        { 
        }
        private static SimpLeMath simple { get; set; }

        public static SimpLeMath GetSimpLeMath(){
            if(simple==null){
                simple=new SimpLeMath();
                return simple;
            }else{
                return simple;
            }
        }
    }

按照以上代码 构造函数私有时,实例不可以创建只有调用自身的GetSimpleMath()方法。

静态类不可以new实例,功能公开。

OOP支柱

个核心:封装 继承 多态

封装就是将实现细节隐藏起来,也起到了数据保护的作用。

继承就是基于已有类来创建新类可以继承基类的核心功能。

另外一种代码重用是:包含/委托,这种重用不是父子类的继承关系,而是一个类定义为另一个类的成员变量。

C#基础

一个简单的例子,汽车和收音机,汽车里面有一个收音机,不能说继承,不符合逻辑,只能说是一种对象包含了另一个对象。汽车对象中调用了收音机对象中的方法。

多态

同一种方式处理相关对象的能力。

相同的请求操作不同。

两个原则:里氏替换原则:子类对象能够替换其基类对象被使用。

开放封闭原则:对外扩展开放,对内修改封闭。

定义一个抽象方法,派生类重写抽象方法具体实现。

C#基础

 C#基础

Aniaml an=new Cat();

通过创建an 就可以指向多个派生类对象。

an= new dog();

an=new chicken();

创建cat对象,基类指向子类

 多态的实现:

C#基础

访问修饰符记下三个常用的

C#基础

Public公共的没有限制

Private私有的 本类可用

Protected 本类和子类使用

默认修饰符都是私有的。

Set get方法就是最基础的封装。

.NET语言提倡使用属性来强制封装状态数据。属性会总是映射到“实际的”访问方法和修改方法。创建了属性会在背后调用相应的get set来封装。

我查了一下封装的意义

 C#基础

还有一点是

 C#基础

你可以控制它的功能

使用类的属性(不太清楚含义)

属性内部表示

如果已经写了封装set get方法又重新定义了set get方法那么编译会出错,基础类库会使用类型属性而不是传统的访问和修改方法。

Class employee
{
Public static companyName{get;set;}
简单写法
}
Class employee
{
Private static string companyName;//对字段封装
Public staric string company//封装没有括号()
{
 Get{return companyName;}
 Set(companyName=value;)
}
}
Main(){
Employee.company=”My company”;
}

自动属性

 C#基础

比较方便。但自动属性必修同时支持读写功能。

Const用来定义常亮

比如我定义一个π值

Public const double pai=3.14;

Ps:关键字都是加字数据类型前面

Readonly只读字段 和常量相似 不能在赋初始值后改变。

但和常量也有不同的地方。

赋给只读字段的值可以在运行时决定,因此在构造函数中赋值是合法的。例如:

Class{

Public readonly double pi;

Public class(){

Pi=3.14;

}

}

继承与多态

C#中定义冒号:操作类之间的关系。

例如:MiVan继承Car

Class MiVanCar

{

}

MiVan可以调用父类的公共属性,继承保护了封装,无法访问私有成员。

.NET不允许多重继承

可以将类、结构或接口的定义拆分到两个或多个源文件中,在类声明前添加partial关键字即可。

Sealed关键字

防止发生继承

创建子类对象访问基类功能,并扩展。

Base关键字

静态成员只能由类来访问,不能由对象来访问。

Basethis都是访问类的实例

一般基类的默认构造函数会被自动调用

 C#基础

上述例子,显示调用合适的自定义基类构造函数。解决只读问题和低效性。增加了自定义的构造函数,就把默认的构造函数自动移除了,所以得重新定义默认构造函数。

C#基础

基类加Protected关键字

好处:派生类就可以直接访问基类的信息。

坏处:有可能绕过公共属性设置的业务规则。

受保护的数据被认为是私有的。

包含/委托编程

Has-a关系

简单的说,委托就是增加公共成员到包含类,以便使用被包含对象的功能。

 

非嵌套类不能使用private关键字声明。

Public class OuterClass

{

//公共嵌套类型可以被任何人使用

Public class PublicInnerClass()

//私有嵌套类型只可以被包含类的成员使用

Private class PrivateInnerClass()

}

C#多态

相同的请求做出不同的响应。

如果基类希望定义由子类重写的方法,必须加上virtual关键字。

如果子类希望改变虚方法的实现细节,必须使用override关键字。

可以通过base关键来使用默认行为

例如:

Base.GiveBonus();

使用了基类的默认行为。

抽象类

防止创建模糊的基类,使用abstract关键字。

Abstract class Employee

{

}

构建多态接口

抽象方法只可以在抽象类中

dynamic 类似于javascript中的Var 弱类型(与底层打交道会用的到)

接口

接口是一组抽象成员的集合。

C# 用interface来定义接口。

接口中不能有字段,也不能有构造函数,接口不能提供实现。

接口可以实现多继承
as关键字判断是否支持一个接口 null
is关键字判断是否实现一个接口 false

接口在以下情况特别有用:
只有一个层次结构,但是只有一部分派生类支持某个公共行为
需要构建的公共行为跨多个层次。目前没有理解

接口还没有具体的写过(理解还不深)

例如:接口类ICamera

VirtualCamera 继承ICamera();

ICamera camera=new VirtualCamera();

调用camera的方法。

泛型

泛型是个容器

装箱 将值类型存放在栈上的 转换成存在堆上的引用类型

拆箱 反过来

与非泛型容器相比,提供更好的性能,不会导致装箱或拆箱的损耗

泛型更类型安全,因为只包含我们指定的类型

泛型有多种 Queue stack 等泛型类型 。还有种可以自动排序的SortedSet泛型。

委托

本质上讲 委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法。通俗的讲就是传方法。
包含3点
1 它所调用的方法的名称
2 该方法的参数
3 该方法的返回值
关键字为delegate

下面给个委托的简单例子:

public delegate int BinaryOp(int x, int y);

    public class SimpLeMath
    {
        public int Add(int x, int y)
        {
            return x + y;
        }
        public int Subtract(int x, int y)
        {
            return x - y;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("************delegate************");
            SimpLeMath a = new SimpLeMath();
            BinaryOp b = a.Subtract;//给委托一个方法名
            Console.WriteLine("10+10 is {0}", b(10, 10));
            Console.ReadLine();

        }
    }

委托BinaryOp b指向方法Subtract 这样b就代替了方法Subtract。

具体的实例 :https://www.cnblogs.com/Alex-bg/archive/2012/05/13/2498043.html 参考这篇文章

委托还可以多路广播,一个委托对象可以维护一个调用方法的列表。使用+=操作符。按照方法列表的顺序执行方法。-=用来移除方法。

委托协变

C#基础

 通过委托实现继承的感觉,类似于里氏替换原则。

线程与进程

进程和线程放在一起谈比较好理解。

进程可理解为一个应用程序。一个进程中只有一个任务,就是单线程,有多个任务就是多线程。

下面附上一个线程的小例子:

//打印
 public class Printer
    {
        public void PrintNumbers()
        {
            Console.WriteLine("->{0} is executing PrintNumbers()", Thread.CurrentThread.Name);

            Console.Write("Your numbers: ");
            for (int i = 0; i < 5; i++)
            {

                Console.Write("{0}", i);
                Thread.Sleep(2000);//间隔2秒
            }

        }

    }
class Program
    {
        static void Main(string[] args)
        {
            Printer print = new Printer();
            ThreadStart thread = print.PrintNumbers;//ThreadStart委托
            Thread t = new Thread(thread);
            t.IsBackground = true;//默认为前台线程,不会随Main()方法的结束而结束。可以设置thread.IsBackground为True,则转为后台线程,即可随Main()方法的结束而结束。
            t.Start();
            //Thread t = new Thread(new ThreadStart(print.PrintNumbers));
            //t.Name = "Thread";
            //t.Priority = ThreadPriority.Highest;
            //t.Start();
//第一种写的清楚点,第二种写的简易。
            MessageBox.Show("************test************");
        }
    }

 开启一个线程运行Print操作 同时也显示了提示信息。如果不用线程则将等Print结束后才可以显示提示信息。

前后台线程在进行解释:前台线程可以阻止应用程序的终结,要一直等到所有前台线程结束方可终结,而后台线程当应用程序终结时,所有的后台线程也自动终止。

创建10个线程

Thread [] threads= new Thread[10];
for(int i=0;i<10;i++)
{
threads[i]=new Thread(new ThreadStart(XX方法名));
}

并发问题

多线程共同使用某个共享资源的时候,会造成冲突。

举个生活例子以及解救方法

生活中我们会遇到这样的情况:去商场买衣服时,如果想要试一试是否合身,一般可以利用商场提供的试衣间,但一个试衣间一个时间段内只能有一个人在试衣。如果有多个人都要使用同一个试衣间(共享资源),而试衣间的门又没有加锁,则会存在共享资源的冲突问题。
实际生活中,这个问题是这样解决的:
现在假设有A、B、C同时要使用试衣间,A进去后,将门锁上,B看到门已上锁,则知道有人在试衣,会进行等待,A使用完试衣间后,打开锁(解除对共享资源的锁定),则B知道此时试衣间可以使用,则B使用试衣间。C同样。
 简单的一把锁,解决了试衣间的使用冲突问题。

在代码中使用lock关键字来进行锁定。

public class Printer
    {
        private object threadLock = new object();

        public void PrintNumbers()
        {
            lock (threadLock)//所有的代码都必须在锁定范围中
            {
                Console.WriteLine("->{0} is executing PrintNumbers()", Thread.CurrentThread.Name);

                Console.Write("Your numbers: ");
                for (int i = 0; i < 5; i++)
                {
                    Console.Write("{0}", i);
                    Thread.Sleep(2000);
                }
            }
        }
 
    }

相关推荐