可供参考的别人的学习笔记:https://blog.csdn.net/Jacey_cai/article/details/122848272
1.console :控制台应用(.NET Framework)
2.WPF:WPF应用程序,是新的Windows Forms
3.Windows Forms :Windows窗体应用(无后缀)
4.ASP.NET Web Forms(Old):ASP.NET Web应用程序(.NET Framework)->Web Forms
5.ASP.NET MVC(Model-View-Contoller),ASP.NET Web应用程序(.NET Framework)->MVC,是新的ASP.NET Web Forms,它可以让程序员把不同种类的代码分开放到不同文件里面
6.WCF:WCF服务应用程序,纯网络服务
7.Wiindows Store Application:给平板电脑写,已凉
8.Windows Phone Application:已凉
9.Cloud(Windows Azure)
10.WF(Workflow Foundation):工作流
类构成程序的主体
名称空间以树型结构组织类(和其他类型)
孩子是引用变量,气球是实例,多个孩子通过一根绳子牵着气球用到ref
以属性为侧重点的类,以方法为侧重点的类,以事件为侧重点的类,默写。
//重在事件的代码演示
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WpfApp4
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();//实例化
timer.Interval = TimeSpan.FromSeconds(1);//给属性值,1s钟生成一个timespan
timer.Tick += Timer_Tick;//挂接事件处理器
timer.Start();//方法调用
}
private void Timer_Tick(object sender, EventArgs e)//事件处理器
{
this.timeTextBox.Text = DateTime.Now.ToString();
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
}
https://www.bilibili.com/video/BV13b411b7Ht/?p=4&spm_id_from=pageDriver&vd_source=04d791790ccb453dc9e7113022339213
要慎用事件机制
鼠标移动到类上,按下F1键,就会自动打开MSDN文档,显示当前类的信息
如果想看这个类在什么分支上,可以点一下内容同步
输入cw+Tab+Tab就可以打出Console.WriteLine,快捷键。
静态(Static)成员在语义上表示他是“类的成员”
实例(非静态)成员在语义表示他是“对象的成员”
在MSDN里面如果发现某个属性下面加了一的大写的S,就说明他是一个静态的属性。
绑定(Binding)指的是编译器如果把一个成员与类或对象关联起来
比如在编写console类和Form类的时候,进行编译时,编译器知道把某个成员是跟这个类关联起来,还是跟这个类的对象关联起来,叫做Binding。早绑定指的是编译器在编译这个类的时候就已经知道这个成员是隶属于这个类还是隶属于这个类的对象,晚绑定是编译器不管这个事情,当程序运行起来之后,才由程序员决定这个方法属于这个类还是这个类的对象,有晚绑定功能的语言一般叫做动态语言,比如JavaScript
构成C#语言的基本元素
var能推断赋值的类型,一般都是使用明确的
方法的声明和调用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
Calculator c = new Calculator();
int x = c.Add(2, 3);
Console.WriteLine(x);
string str = c.Today();
Console.WriteLine(str);
c.PrintSum(4, 6);
}
}
class Calculator
{
public int Add(int a, int b)//有数据输入输出
{
int result = a + b;
return result;
}
public string Today()//无数据输入有输出
{
int day = DateTime.Now.Day;
return day.ToString();
}
public void PrintSum(int a, int b)//有数据输入无输出
{
int result = a + b;
Console.WriteLine(result);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
Calculator c = new Calculator();
c.PrintXTo1(10);
c.PrintXTo1d(3);
}
}
class Calculator
{
public void PrintXTo1(int x)//循环
{
for (int i = x; i > 0; i--)
{
Console.WriteLine(i);
}
}
public void PrintXTo1d(int x)//调用递归
{
if (x == 1)
{
Console.WriteLine(x);
}
else
{
Console.WriteLine(x);
PrintXTo1(x - 1);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
Calculator c = new Calculator();
int result = c.SumFrom1ToX(100);
Console.WriteLine(result);
}
}
class Calculator
{
//public int SumFrom1ToX(int x)//循环
//{
// int result = 0;
// for (int i = 1; i <= x; i++)
// {
// result = result + i;
// }
// return result;
//}
//public int SumFrom1ToX(int x)//递归
//{
// if (x == 1)
// {
// return 1;
// }
// else
// {
// int result = x + SumFrom1ToX(x - 1);
// return result;
// }
//}
public int SumFrom1ToX(int x)
{
int result = (1 + x) * x / 2;
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp5
{
class Program
{
public const int MAX_VALUE = 70;
static void Main(string[] args)
{
Calculator nu = new Calculator();
int levels = 0;
Console.Write($"输入汉诺塔层数(1~{MAX_VALUE}):");
levels = int.Parse(Console.ReadLine());
if (levels > 0 && levels < MAX_VALUE)
{
nu.Hanoi(levels, 'a', 'b', 'c');
nu.ReportNum();
Console.ReadKey();
}
else
{
Console.WriteLine("输入范围错误");
Console.ReadKey();
}
}
}
class Calculator
{
public static int num = 0;
public void Hanoi(int n, char a, char b, char c)
{
if (n == 1)
{
Console.WriteLine($"{a}--->{c}");
num++;
return;
}
else
{
Hanoi(n - 1, 'a', 'c', 'b');
Hanoi(1, 'a', 'b', 'c');
Hanoi(n - 1, 'b', 'a', 'c');
}
}
public void ReportNum()
{
Console.WriteLine($"一共移动了{num}次");
}
}
}
冯诺依曼系统(计算机组成原理课程,存储器对应内存 ,硬盘是扩展存储器,固态SSD硬盘更快)
有些编程语言是强类型编程语言,有些是弱类型编程语言
C#语言是强类型语言
int x;
x=100L;//会报错,保护类型正确
x=100;
if(x=200)//报错,C语言中不报错
//vs创建C语言项目:https://blog.csdn.net/weixin_58396509/article/details/125949295
C#语言提供了一种机制,动态类型,用来模仿弱类型
dynamic myVar = 100;
Console.WriteLine(myVar);
myVar = "Mr.Okey!";
Console.WriteLine(myVar);
使用var定义变量时有以下四个特点:
//WPF程序,演示内存堆的占用和释放
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp5
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
List<Window> winList;
private void Button1_Click(object sender, RoutedEventArgs e)
{
winList = new List<Window>();
for (int i = 0; i < 15000; i++)
{
Window w = new Window();//因为Window的实例占的内存比较多
winList.Add(w);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
winList.Clear();
}
}
}
https://www.bilibili.com/video/BV13b411b7Ht/?p=6&spm_id_from=333.880.my_history.page.click&vd_source=04d791790ccb453dc9e7113022339213视频末尾演示监控内存(堆)
光标移动到类名,按F12打开类定义。值类型是存储在内存的栈里面,因为值类型的大小确定,切语句运行完就释放,引用类型是存在堆里面。
第一组关键字:object和string这两个类太常用了,所以C#吸收为关键字了;class、Interface、delegate这三个关键字是用来定义自己的数据类型的。
第二组关键字代表值类型,蓝色关键字是真正的数据类型。
第三组关键字中var和dynamic是用来声明变量的。
基本数据类型也叫内建数据类型。int、short等都是结构体定义。
局部变量指在方法体和函数体中声明的变量。
字段是属性的雏形,如果裸露字段,那么就有可能会给成员变量赋一个不合理的值,比如age=-1。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
Student.Amount;
Student stu = new Student();//实例变量
int[] array = new int[100];//声明一个长度为100的整型数组,分出400个字节
}
}
class Student
{
public static int Amount;//静态变量是隶属于这个类的,不是隶属于类的实例的
public int Age;
public string Name;
public double Add(double a, double b)//a,b就叫做值参数变量,也叫做形参
//public double Add(ref double a, double b),如果在参数前面加一个ref就是引用参数变量,加一个out为输出参数变量
{
double result = a + b;//result为局部变量
return result;
}
}
}
值类型没有实例,所谓的实例和变量合二为一,意思是:
int x = new int();与int x;的效果相同
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
Student Stu;//引用类型变量所存放的是引用类型实例在堆中的地址
Stu = new Student();//在堆里分配4+2=6个字节内存
}
}
class Student
{
uint ID;
ushort Score;
}
}
实例是分配在堆上的,类里面的成员变量,也叫字段,他不是局部变量,而是随着类的实例化而在堆上分配内存,再把分配的地址交还给引用实例变量
在C#中,局部变量如果没有赋值的话,是不能通过编译的,但是其他类里面的成员变量,默认值0
const修饰符,常量,初始化器也是不能省略的,const int x = 100;
装箱和拆箱本质上就是值类型和引用类型之间的转换
装箱:当我们的计算机发现,这个object类型变量obj所要引用的这个值,他不是堆上的实例,而是一个栈上的值类型的时候,他会先把值类型的值copy一下,在对上面找一块可以存储的空间,把这个空间变成一个对象,再把这个对应的起始是地址转化为2进制,存储到obj这个变量所对应的内存空间里面来,从而构成obj这个引用变量对堆上实例的引用,而这个实例封装着我们的一个整数,就叫做装箱。装箱会损失程序的性能。简单说就是:装箱是把栈上的值类型的值封装成一个object类型的实例放在堆上;拆箱就是把堆上object实例的值按照要求拆成目标数据类型,存储到栈上去;装箱拆箱会损失程序的性能。
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
int x = 100;
object obj;
obj= x; //叫做装箱,obj叫做引用类型,但内存中分配四个字节的存储单元
int y = (int)obj;//叫做拆箱
}
}
Visual C+±>Empty Project
在文件夹shift+右击能快速点出PowerShell命令行界面
当一个函数以类的成员的身份出现的时候就成为方法,这就是为什么方法有一个别名叫做成员函数。
C#中类的定义和声明是放在一起的,C++是分开的。
静态方法是程序运行的时候会直接加载到内存里面,全部static的话会影响程序性能。
在方法名后面加一对圆括号(不能省略),在括号里面写入必要的实际参数(argument),这对圆括号在C#里面叫做方法调用操作符。声明时参数是形参(parameter),调用时参数是实际参数(argument)
构造函数译为构造器,成员函数译为方法,本质都是函数。
小技巧:输入ctor,再按两下tab即可自动生成构造函数的代码片段
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student(2,"Mr.okey");//这个括号实际上就是在调用构造器
Console.WriteLine(stu.ID);//值为0,说明已经调用了构造器
Console.WriteLine(stu.Name);
Console.WriteLine("**************");
Student stu2 = new Student();
Console.WriteLine(stu2.ID);
Console.WriteLine(stu2.Name);
}
}
class Student//当你声明一个类后,你又没有为他准备构造器的话,编译器会为他准备一个默认的构造器
{
public Student()//构造器,没有返回值
{
this.ID = 1;
this.Name = "No name";
}
public Student(int initId, string initName)//带参数构造器
{
this.ID = initId;
this.Name = initName;
}
public int ID;//创建两个公开字段
public string Name;
}
}
下面是构造器的内存原理:
注意栈的内存分配的时候是由比较高的地址向比较低的地址分配,直至溢出,先进后出。一个整形默认的话都是0,字符串类型都是0的话是Null值。
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}public int Add(int a, int b, int c)
{
return a + b + c;
}
public int Add<T>(int a, int b)//是泛型,叫做类型参数,未来会参与到方法里面来,与C++的模板相似
{
T t;
return a + b;
}
public int Add(ref int a,int b)//ref代表传递引用参数,与C++中&类似,out为输出参数
{
return a + b;
}
public int Add(out int a,int b)//out为输出参数,参数的种类不同,可以重载
{
return a + b;
}
第二条观察方法调用的时的call Stack,可以观察方法的调用逻辑
F11为逐语句,F10逐过程,可以快速的大范围的定位,如果调试已知某些方法正确,那么为了节省时间,可以用F10跳过正确部分。F10和F11交替使用进行调试。Shift+F11跳出,可以快速返回当前方法的调用处,这样比在call stack中点击,再在调用这个方法的位置处设置断点,要快很多。
详细的方法调用时栈的存储情况见视频末尾:https://www.bilibili.com/video/BV13b411b7Ht/?p=9&spm_id_from=pageDriver&vd_source=04d791790ccb453dc9e7113022339213
返回值一般存放在cpu的寄存器里面,可以理解为内嵌在cpu当中非常快速的内存,当寄存器存不下这个值时,也会在栈上开辟空间,返回参数后,相应的stack frame就被清空了,传递进来的参数也就被清空了。
从上往下优先级依次降低,同级运算符优先级按照数学中从左到右的顺序,但是最后一行赋值是从右到左运算。
为了说明C#中操作符就是函数的简记法,此处与C++类似(C++中叫做符号的重载)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp8
{
class Program
{
static void Main(string[] args)
{
Person person1 = new Person();
Person person2 = new Person();
person1.Name = "Deer";
person2.Name = "Deer's wife";
List<Person> nation = person1 + person2;
foreach (var p in nation)
{
Console.WriteLine(p.Name);
}
}
}
class Person
{
public string Name;
public static List<Person> operator+(Person p1, Person p2)
{
List<Person> people = new List<Person>();
people.Add(p1);
people.Add(p2);
for (int i = 0; i < 11; i++)
{
Person child = new Person();
child.Name = p1.Name + "&" + p2.Name + "'s child";
people.Add(child);
}
return people;
}
}
}
基本表达式:最基础的,不能够在进行拆分的表达式,基本运算符就是用来参与基本表达式的运算符。
表达式就是表达一定算数意图的式子。
System.IO.File.Create("D:\\HelloWorld.txt");//1,2,3
Form myForm = new Form();
myForm.Text = "Hello,World!";//4
myForm.ShowDialog();//4
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
//声明数组的三种方法:
int[] myIntArray1 = new int[10];
int[] myIntArray2 = new int[] { 1, 2, 3, 4, 5 };//这对花括号叫做初始化器,不叫构造器
int[] myIntArray3 = new int[5] { 1, 2, 3, 4, 5 };
//访问数组中的元素
Console.WriteLine(myIntArray3[0]);
Console.WriteLine(myIntArray3[myIntArray3.Length-1]);
}
}
}
字典索引:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
//泛型类不是一个完整的类,他需要和其他的类型组合在一起
//string作为索引的类型,Student作为值的类型
Dictionary<string, Student> stuDic = new Dictionary<string, Student>();
for (int i = 0; i < 100; i++)
{
Student stu = new Student();
stu.Name = "s_" + i.ToString();
stu.Score = 100+i;
stuDic.Add(stu.Name,stu);
}
Student number6 = stuDic["s_6"];//字典索引
Console.WriteLine(number6.Score);
}
}
class Student
{
public string Name;
public int Score;
}
}
y=x++;
//相当于:
y=x;
x=x+1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp10
{
class Program
{
static void Main(string[] args)
{
Type t = typeof(int);
Console.WriteLine(t.Namespace);
Console.WriteLine(t.FullName);
Console.WriteLine(t.Name);
int c = t.GetMethods().Length;
foreach (var mi in t.GetMethods())
{
Console.WriteLine(mi.Name);//打印出类中所有方法的名字
}
Console.WriteLine(c);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleApp10
{
class Program
{
static void Main(string[] args)
{
int x = default(int);//结构体类型,内存块都写0,值为0
Console.WriteLine(x);
Form myForm = default(Form);//引用类型,内存块都写0,就是空值
Console.WriteLine(myForm == null);
Level level = default(Level);
Console.WriteLine(level);//枚举类型,注意枚举类型中若无0对应时,默认输出0,而不是枚举的
}
enum Level//枚举类型在声明时会和整数值对应起来,Low=0
//在设定枚举类型时最好给一个o值,因为默认输出0对应
{
Low,//也可以显式赋值:Low=1
Mid,
High
}
}
}
static void Main(string[] args)
{
/Form myForm = new Form();//()为调用实例构造器
myForm.Text = "Hello";
myForm.ShowDialog();
//下面{ }内为调用初始化器
Form myForm = new Form() { Text = "Hello",FormBorderStyle = FormBorderStyle.SizableToolWindow };//取消最大化最小化按钮
myForm.ShowDialog();
}
有时候,某些实例只使用一次,也可以这么写:
new Form() { Text = "Hello" }.ShowDialog();
下面有一个问题:是不是创建实例都要用new运算符呢?
不是的,比如:
int x = 100;//int是一个结构体
string name = "Tim";//string是一个类,name是一个实例,这里创建实例没有用new
string name = new string("Tim");//与上面语句作用一样,一般不这么用
string这个类型用起来和int是一样的格式,这是因为微软在创建C#语言的时候为了同意这些基本类型的使用体验,故意把这个new操作符给隐藏起来了,在用string时就像int一样用就可以了,就简化了,这叫做语法糖衣,同样的还有创建数组的时候:
int[] myArray = new int[10];//创建int类型的实例的时候,这里不用在后面加(),因为它的构造器比较特殊
//也可以用这样的语法:
int[] myArray = {1, 2, 3, 4};//这里我们也没有用到new操作符,这也是一个语法糖衣
另外一个new操作符的特殊且有意思的用法:为匿名类型创建对象
namespace ConsoleApp11
{
class Program
{
static void Main(string[] args)
{
Form myForm = new Form() { Text = "Hello"};//new操作符后面跟一个数据类型,这是针对非匿名类型这么用
var person = new { Name = "Mr.Okey", Age = 34 };//记住这种new操作符和ver组合的使用方法,是为匿名类型创建对象,并且用隐式类型变量来引用实例。
Console.WriteLine(person.Name);
Console.WriteLine(person.Age);
Console.WriteLine(person.GetType().Name);
}
}
}
编译结果为:
其实这是一种合理的“偷懒”办法,而且比较鼓励使用var来声明变量。
记住这种new操作符和ver组合的使用方法,是为匿名类型创建对象,并且用隐式类型变量来引用实例。
在使用new运算符时一定要小心,一旦在一个类里面调用了类运算符,你正在编写的这个类型和你创建实例的这个类型之间就构成了十分紧密的耦合。(new操作符功能强大,但是不能乱用,在编写大型程序的时候,为了避免new操作符造成的紧耦合,我们有一种依赖注入的设计模式去使用)。
上述为new作为操作符时的用法。
namespace ConsoleApp11
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu.Report();
CsStudent csStu = new CsStudent();
csStu.Report();
}
}
class Student
{
public void Report()
{
Console.WriteLine("I'm a student");
}
}
class CsStudent : Student//继承自父类Student
{
new public void Report()//这里new为子类对父类的方法进行隐藏,这里new不叫操作符,叫做修饰符
{
Console.WriteLine("I'm CS student");
}
}
}
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
uint x = uint.MaxValue;
Console.WriteLine(x);
string binStr = Convert.ToString(x, 2);
Console.WriteLine(binStr);
uint y = x + 1;//会变成0
try
{
uint y2 = checked(x + 1);//程序会产生溢出异常
Console.WriteLine(y);
}
catch (OverflowException ex)//用来抓住产生的这个异常
{
Console.WriteLine("There's overflow");
}
}
}
}
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
uint x = uint.MaxValue;
Console.WriteLine(x);
string binStr = Convert.ToString(x, 2);
Console.WriteLine(binStr);
checked//checked后面会有一个语句块,在这个语句块的上下文范围之内,所有溢出都是会被检测到的,unchecked就是不检查
{
try
{
uint y = x + 1;
Console.WriteLine(y);
}
catch (OverflowException ex)
{
Console.WriteLine("There's overflow");
}
}
}
}
}
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
int x = sizeof(decimal);
Console.WriteLine(x);
unsafe//还需要在项目-项目属性-build-允许unsafe
{
int y = sizeof(Student);
Console.WriteLine(y);//y=16,因为内存对齐
}
}
}
struct Student
{
int Id;//4
long Score;//8
}
}
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
unsafe
{
Student stu;
stu.ID = 1;
stu.Score = 99;
Student* pstu = &stu;
pstu->Score = 100;
Console.WriteLine(stu.Score);//凡是通过stu.去访问的都是直接访问,而用pstu->去访问的都是通过指针的间接访问
}
}
}
struct Student
{
public int ID;//4
public long Score;//8
}
}
只要有一个操作数跟在操作符后面即可,也叫一目操作符
Student stu;
stu.ID = 1;
stu.Score = 99;
Student* pstu = &stu;//
pstu->Score = 100;
(*pstu).Score = 101;//
Console.WriteLine(stu.Score);
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
int x = 100;
int y = -x;//注意正负界限不对称
Console.WriteLine(y);
string xStr = Convert.ToString(x, 2).PadLeft(32, '0');
Console.WriteLine(xStr);
int x2 = 12345678;
int y2 = ~x2;
string x2Str = Convert.ToString(x2, 2).PadLeft(32, '0');//32位显示,不满则用0左侧补齐
string y2Str = Convert.ToString(y2, 2).PadLeft(32, '0');
Console.WriteLine(x2Str);
Console.WriteLine(y2Str);
}
}
}
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student(null);
Console.WriteLine(stu.Name);
}
}
class Student
{
public Student(string initName)
{
if (!string.IsNullOrEmpty(initName))//如果非空,!操作符在现实的应用
{
this.Name = initName;
}
else
{
throw new ArgumentException("initName cannot be null or empty.");//否则抛出异常
}
}
public string Name;
}
}
class Program
{
static void Main(string[] args)
{
int x = 100;
int y = ++x;
Console.WriteLine(x);//101
Console.WriteLine(y);//101
}
}
class Program
{
static void Main(string[] args)
{
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
int x = Convert.ToInt32(str1);
int y = Convert.ToInt32(str2);
Console.WriteLine(str1 + str2);
Console.WriteLine(x+y);
}
}
不丢失精度的转换:
int x = int.MaxValue;
long y = x;
子类向父类的转换:
namespace ConsoleApp13
{
class Program
{
static void Main(string[] args)
{
Teacher t = new Teacher();
//C#语言规定,当你试图拿一个引用变量去访问它所引用的实例的成员的时候,
//这个时候只能访问到这个变量的类型,它所具有的成员。是你这个变量的类
//型,而不是它所引用的实例的类型
Human h = t;
h.Think();
Animal a = h;
a.Eat();
}
}
class Animal
{
public void Eat()
{
Console.WriteLine("Eating...");
}
}
class Human : Animal
{
public void Think()
{
Console.WriteLine("Who I am?");
}
}
class Teacher:Human
{
public void Teach()
{
Console.WriteLine("I teach programming");
}
}
}
显示类型转换:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ushort.MaxValue);
uint x = 65536;
ushort y = (ushort)x;//32位的数据塞到16位的空间里面,只能舍弃高16位,y=0
Console.WriteLine(y);
}
}
string类到数值类的转换需要用工具–convert类实现
int x = Convert.ToInt32("32");
Console.WriteLine(x);
数值类到string类的转换有两种方法:
调用数值类型数据的ToString方法(一般应用在想让一个文本框显示一个数值的时候,WPF)
所有的数据类型都属于object类型的子类,object类型就带有四种方法,所以所有的数据类型都有这四种方法:
实际上,在目标数据类型存在着Parse方法,但是它有个缺陷,就是只能解析格式正确的字符串数据类型
为了让程序看起来更加友好,目标数据类型往往准备了另外一个方法TryParse方法
它通过输出类型参数来输出转换结果,等学到输出类型参数时再深入。
操作符本质是方法的简记法,对于类型转换操作符也是这样,其背后的秘密:(与C++中的操作符重载一样)
namespace ConsoleApp13
{
class Program
{
static void Main(string[] args)
{
Stone stone = new Stone();
stone.Age = 5000;
Monkey wukongSun = (Monkey)stone;
//隐式类型转换写法:
//Monkey wukongSun = stone;
Console.WriteLine(wukongSun.Age);
}
}
class Stone
{
public int Age;
//显示类型转换用explicit,隐式类型转换用implicit
public static explicit operator Monkey(Stone stone)
{
Monkey m = new Monkey();
m.Age = stone.Age / 500;
return m;
}
}
class Monkey
{
public int Age;
}
}
务必留意**“数值提升”**
乘法分为:整数乘法、浮点乘法、小数乘法。见C#标准文档7.8节。
double*int,结果为double类型,这就是数据提升。
浮点除法没有除数不能为0的限制,结果为无穷,如果想用正无穷大和负无穷大:
double a = double.PositiveInfinity;//正无穷大
double b = double.NegativeInfinity;//负无穷大
加法还有枚举加法、字符串加法、委托加法。加减也会有类型提升。
具体参见C#标准文档。
<<、 >>,Ctrl+D快捷键:快速复制一行
static void Main(string[] args)
{
int x = 7;
int y = x << 1;
string strX = Convert.ToString(x, 2).PadLeft(32, '0');
string strY = Convert.ToString(y, 2).PadLeft(32, '0');
Console.WriteLine(strX);
Console.WriteLine(strY);
}
在没有溢出的情况下,左移1就是乘2,左移2就是乘2再乘2,右移就是除2。注意:左移时补进来的都是0,右移时最高位,当为正数时补进来的为0,为负数时补进来的为1;
static void Main(string[] args)
{
char char1 = 'a';
char char2 = 'A';
ushort u1= (ushort)char1;
ushort u2 = (ushort)char2;
Console.WriteLine(u1);
Console.WriteLine(u2);
}
字符串不能够比大小,只能够用来比相等不相等
static void Main(string[] args)
{
string str1 = "abc";
string str2 = "Abc";
Console.WriteLine(str1.ToLower() == str2.ToLower());
string.Compare();//也可以调用这个方法去比较,他会有一个返回整数值,如果是0就是相等,如果是负值就是第一个小于第二个,如果是正值就是第一个大于第二个。
}
因为他们都是操作数据的二进制结构,所以也叫做位与、位异或、位或
static void Main(string[] args)
{
int x = 7;
int y = 28;
int z = x & y;
string str1 = Convert.ToString(x, 2).PadLeft(32, '0');
string str2 = Convert.ToString(y, 2).PadLeft(32, '0');
string str3 = Convert.ToString(z, 2).PadLeft(32, '0');
Console.WriteLine(str1);
Console.WriteLine(str2);
Console.WriteLine(str3);
}
&&和||用来操作bool值 ,条件与和条件或有短路效应
static void Main(string[] args)
{
int x = 3;
int y = 4;
int a = 3;
if (x>y && a++>3)
{
Console.WriteLine("Hello");
}
Console.WriteLine(a);
}
&&左边为假,就不再执行右面,||左面为真就不再执行右面
static void Main(string[] args)
{
//int x;
//x = null;会报错
// Nullable x = null;//可空int
//因为可空类型很常见,所以将以用关键字替代
int? x = null;
x = 100;
Console.WriteLine(x);
Console.WriteLine(x.HasValue);
int y = x ?? 1;
Console.WriteLine(y);//若x为null,就给y赋值1
}
条件操作符是所有操作符中唯一一个可以接收三个操作数的操作符,本质上就是if-else分支的简写。
static void Main(string[] args)
{
int x = 80;
string str = string.Empty;
str = (x >= 80) ? "Pass" : "Failed";//条件成立则等于左边,不成立则等于右边,往往把条件括起来以提高可读性。
}
*=、 =、 /=、 %=、 +=、 -=、 <<=、 >>=、 &=、 ^=、 |=、 =>
lambda表达式先不讲
x+=1就是x=x+1
x<<=2就是x=x<<2
赋值运算符的运算顺序为从右到左
int x = 5;
int y = 6;
int z = 7;
int a = x += y *=z;//a为47