赞
踩
菜鸟教程:https://www.runoob.com/csharp/csharp-intro.html
C# 基于 C 和 C++ 编程语言,是一个简单的、现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。
C# 是由 Anders Hejlsberg 和他的团队在 .Net 框架开发期间开发的。
C# 是专为公共语言基础结构(CLI)设计的。CLI 由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。
C# 成为一种广泛应用的专业语言的原因:
C# 一些重要的功能:
创建 C# 编程所需的工具。
C# 是 .Net 框架的一部分,且用于编写 .Net 应用程序。因此,先了解一下 C# 与 .Net 框架之间的关系。
.Net 框架(.Net Framework)
.Net 框架是一个创新的平台,能帮您编写出下面类型的应用程序:
.Net 框架应用程序是多平台的应用程序。
框架的设计方式使它适用于下列各种语言:C#、C++、Visual Basic、Jscript、COBOL 等等。
所有这些语言可以访问框架,彼此之间也可以互相交互。
.Net 框架由一个巨大的代码库组成,用于 C# 等客户端语言。下面列出一些 .Net 框架的组件:
几点值得注意:
示例:C# 的最小的程序结构。C# 文件的后缀为 .cs。
using System;
namespace HelloWorldApplication
{
class HelloWorld
{
static void Main(string[] args)
{
/* 我的第一个 C# 程序*/
Console.WriteLine("Hello World");
Console.ReadKey();
//单行注释
}
}
}
程序的第一行 using System; - using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。
下一行是 namespace 声明。一个 namespace 里包含了一系列的类。HelloWorldApplication 命名空间包含了类 HelloWorld。
下一行是 class 声明(声明一个类)。类 HelloWorld 包含了程序使用的数据和方法声明。类一般包含多个方法。方法定义了类的行为。在这里,HelloWorld 类只有一个 Main 方法。
下一行定义了 Main 方法,是所有 C# 程序的 入口点。Main 方法说明当执行时 类将做什么动作。
下一行 /…/ 将会被编译器忽略,且它会在程序中添加额外的 注释。
Main 方法通过语句 Console.WriteLine(“Hello World”); 指定了它的行为。
WriteLine 是一个定义在 System 命名空间中的 Console 类的一个方法。该语句会在屏幕上显示消息 “Hello World”。
最后一行 Console.ReadKey(); 是针对 VS.NET 用户的。这使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
C# 是一种面向对象的编程语言。在面向对象的程序设计方法中,程序由各种相互交互的对象组成。相同种类的对象通常具有相同的类型,或者说,是在相同的 class 中。
标识符
标识符是用来识别类、变量、函数或任何其它用户定义的项目。
在 C# 中,类的命名必须遵循如下基本规则:
示例:Rectangle(矩形),具有 length 和 width 属性,可能需要接受这些属性值、计算面积和显示细节。
using System; namespace RectangleApplication { class Rectangle { // 成员变量 double length; double width; //成员函数 public void Acceptdetails() { length = 4.5; width = 3.5; } public double GetArea() { return length * width; } public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } } class ExecuteRectangle { static void Main(string[] args) {//实例化 Rectangle 类 Rectangle r = new Rectangle(); r.Acceptdetails(); r.Display(); Console.ReadLine(); } } }
变量分为以下几种类型:
1、值类型(Value types)
值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。
值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。
下表列出了 C# 2010 中可用的值类型:
表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸
using System;
namespace DataTypeApplication
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Size of int: {0}", sizeof(int));
Console.ReadLine();
}
}
}
// 运行结果
// Size of int: 4
2、引用类型(Reference types)
指的是一个内存位置。内置的 引用类型有:object、dynamic 和 string。
(1)对象(Object)类型
是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类,Object 是 System.Object 类的别名。
所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。
但是,在分配值之前,需要先进行类型转换。
当一个值类型转换为对象类型时,则被称为 装箱;反之,则被称为 拆箱。
object obj;
obj = 100; // 这是装箱
(2)动态(Dynamic)类型
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:
dynamic <variable_name> = value;
//例如
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
(3)字符串(String)类型
可以给变量分配任何字符串值。
字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。
字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
String str = "runoob.com";
//一个 @引号字符串
@"runoob.com";
//C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如
string str = @"C:\Windows";//等价于 string str = "C:\\Windows";
//@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
用户自定义引用类型有:class、interface 或 delegate。
3、指针类型(Pointer types)
指针类型变量存储另一种类型的内存地址。与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
type* identifier;
//举例
char* cptr;
int* iptr;
下面的实例显示了一个显式的类型转换:
namespace TypeConversionApplication { class ExplicitConversion { static void Main(string[] args) { double d = 5673.74; int i; // 强制转换 double 为 int i = (int)d; Console.WriteLine(i); Console.ReadKey(); } } } //结果: 5673
内置类型转换方法:
一个变量只不过是一个供程序操作的存储区的名字。
在 C# 中,每个变量都有一个特定的类型,类型决定了变量的内存大小和布局。范围内的值可以存储在内存中,可以对变量进行一系列操作。
C# 中提供的基本的值类型大致可以分为以下几类:
1、变量定义
2、变量初始化
3、接受来自用户的值
System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入,并把它存储到一个变量中。
int num;
num = Convert.ToInt32(Console.ReadLine());
函数 Convert.ToInt32() 把用户输入的数据转换为 int 数据类型,因为 Console.ReadLine() 只接受字符串格式的数据。
常量:
常量可以被当作常规的变量,只是它们的值在定义后不能被修改。
常量是使用 const 关键字来定义的 。定义一个常量的语法如下:
const <data_type> <constant_name> = value;
常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。
一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
例如,Nullable< Int32 >,读作"可空的 Int32",可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。
? 单问号用于对 int、double、bool 等无法直接赋值为 null 的数据类型进行 null 的赋值,意思是这个数据类型是 Nullable 类型的。
int? i = 3;
//等同于:
Nullable<int> i = new Nullable<int>(3);
int i; //默认值0
int? ii; //默认值null
null合并运算符**??** 用于定义可空类型和引用类型的默认值;双问号用于判断一个变量在为 null 的时候返回一个指定的值。
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
结构体是值类型数据结构。
它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
结构体是用来代表一个记录。假设您想跟踪图书馆中书的动态。您可能想跟踪每本书的以下属性:
struct 语句为程序定义了一个带有多个成员的新的数据类型。
using System; using System.Text; struct Books { private string title; private string author; private string subject; private int book_id; public void setValues(string t, string a, string s, int id) { title = t; author = a; subject = s; book_id =id; } public void display() { Console.WriteLine("Title : {0}", title); Console.WriteLine("Author : {0}", author); Console.WriteLine("Subject : {0}", subject); Console.WriteLine("Book_id :{0}", book_id); } }; public class testStructure { public static void Main(string[] args) { Books Book1 = new Books(); /* 声明 Book1,类型为 Books */ Books Book2 = new Books(); /* 声明 Book2,类型为 Books */ /* book 1 详述 */ Book1.setValues("C Programming", "Nuha Ali", "C Programming Tutorial",6495407); /* book 2 详述 */ Book2.setValues("Telecom Billing", "Zara Ali", "Telecom Billing Tutorial", 6495700); /* 打印 Book1 信息 */ Book1.display(); /* 打印 Book2 信息 */ Book2.display(); Console.ReadKey(); } }
C# 结构的特点
您已经用了一个简单的名为 Books 的结构。在 C# 中的结构与传统的 C 或 C++ 中的结构不同。C# 中的结构有以下特点:
类 vs 结构
类和结构有以下几个基本的不同点:
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0。
using System; public class EnumTest { enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat }; static void Main() { int x = (int)Day.Sun; int y = (int)Day.Fri; Console.WriteLine("Sun = {0}", x); Console.WriteLine("Fri = {0}", y); } } //结果 Sun = 0 Fri = 5
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C# 有丰富的内置运算符,分类如下:
运算符优先级
无限循环:
for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。
for (; ; )
{
Console.WriteLine("Hey! I am Trapped");
}
C# 封装
C# 方法
C# 数组
C# 类
C# 继承
C# 多态性
C# 运算符重载
C# 接口
C# 正则表达式: https://www.runoob.com/csharp/csharp-regular-expressions.html
C# 异常处理
在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
using System; namespace first_space { class namespace_cl { public void func() { Console.WriteLine("Inside first_space"); } } } namespace second_space { class namespace_cl { public void func() { Console.WriteLine("Inside second_space"); } } } class TestClass { static void Main(string[] args) { first_space.namespace_cl fc = new first_space.namespace_cl(); second_space.namespace_cl sc = new second_space.namespace_cl(); fc.func(); sc.func(); Console.ReadKey(); } }
使用 using 命名空间指令
using System; using first_space; using second_space; namespace first_space { class abc { public void func() { Console.WriteLine("Inside first_space"); } } } namespace second_space { class efg { public void func() { Console.WriteLine("Inside second_space"); } } } class TestClass { static void Main(string[] args) { abc fc = new abc(); efg sc = new efg(); fc.func(); sc.func(); Console.ReadKey(); } }
命名空间可以被嵌套,可以在一个命名空间内定义另一个命名空间,使用点(.)运算符访问嵌套的命名空间的成员。
预处理器指令指导编译器在实际编译开始之前对信息进行预处理。
所有的预处理器指令都是以 #
开始。且在一行上,只有空白字符可以出现在预处理器指令之前。预处理器指令不是语句,所以它们不以分号 ;
结束。
C# 编译器没有一个单独的预处理器,但是,指令被处理时就像是有一个单独的预处理器一样。在 C# 中,预处理器指令用于在条件编译中起作用。与 C 和 C++ 不同的是,它们不是用来创建宏。一个预处理器指令必须是该行上的唯一指令。
C# 预处理器指令列表:
#define DEBUG #define VC_V10 using System; public class TestClass { public static void Main() { #if (DEBUG && !VC_V10) Console.WriteLine("DEBUG is defined"); #elif (!DEBUG && VC_V10) Console.WriteLine("VC_V10 is defined"); #elif (DEBUG && VC_V10) Console.WriteLine("DEBUG and VC_V10 are defined"); #else Console.WriteLine("DEBUG and VC_V10 are not defined"); #endif Console.ReadKey(); } }
一个 文件 是一个存储在磁盘中带有指定名称和目录路径的数据集合。当打开文件进行读写时,它变成一个 流。
从根本上说,流是通过通信路径传递的字节序列。有两个主要的流:输入流 和 输出流。输入流用于从文件读取数据(读操作),输出流用于向文件写入数据(写操作)。
https://www.runoob.com/csharp/csharp-file-io.html
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。
//语法格式
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
.Net 框架提供了两种类型的特性:预定义特性 和 自定义特性。
1、预定义特性(Attribute)
.Net 框架提供了三种预定义特性:
描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
1)参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
2)参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
3)参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
标记了一个条件方法,其执行依赖于指定的预处理标识符。
它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace 。例如,当调试代码时显示变量的值。
[Conditional(
conditionalSymbol
)]
//例如
[Conditional("DEBUG")]
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,
但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
1)参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
2)参数 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(); } } //运行结果 Don't use OldMethod, use NewMethod instead
2、创建自定义特性(Attribute)
.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。
该信息根据设计标准和应用程序需要,可与任何目标元素相关。
创建并使用自定义特性包含四个步骤:
最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。
1)声明自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
在上面的代码中,我们已经声明了一个名为 DeBugInfo 的自定义特性。
2)构建自定义特性
让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:
我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。
每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:
// 一个自定义特性 BugFix 被赋给类及其成员 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute { private int bugNo; private string developer; private string lastReview; public string message; public DeBugInfo(int bg, string dev, string d) { this.bugNo = bg; this.developer = dev; this.lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } }
3)应用自定义特性
通过把特性放置在紧接着它的目标之前,来应用该特性:
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")] [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")] class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")] public double GetArea() { return length * width; } [DeBugInfo(56, "Zara Ali", "19/10/2012")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } }
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
优点:
缺点:
反射(Reflection)有下列用途:
1、查看元数据
使用反射(Reflection)可以查看特性(attribute)信息。
using System; [AttributeUsage(AttributeTargets.All)] public class HelpAttribute : System.Attribute { public readonly string Url; public string Topic // Topic 是一个命名(named)参数 { get { return topic; } set { topic = value; } } public HelpAttribute(string url) // url 是一个定位(positional)参数 { this.Url = url; } private string topic; } [HelpAttribute("Information on the class MyClass")] class MyClass { } namespace AttributeAppl { class Program { static void Main(string[] args) { System.Reflection.MemberInfo info = typeof(MyClass);// System.Reflection 类的 MemberInfo 对象需要被初始化,用于发现与类相关的特性(attribute)。 object[] attributes = info.GetCustomAttributes(true); for (int i = 0; i < attributes.Length; i++) { System.Console.WriteLine(attributes[i]); } Console.ReadKey(); } } } //当上面的代码被编译和执行时,它会显示附加到类 MyClass 上的自定义特性: HelpAttribute
实例:
using System; using System.Reflection; namespace BugFixApplication { // 一个自定义特性 BugFix 被赋给类及其成员 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute { private int bugNo; private string developer; private string lastReview; public string message; public DeBugInfo(int bg, string dev, string d) { this.bugNo = bg; this.developer = dev; this.lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } } [DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")] [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")] class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")] public double GetArea() { return length * width; } [DeBugInfo(56, "Zara Ali", "19/10/2012")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(4.5, 7.5); r.Display(); Type type = typeof(Rectangle); // 遍历 Rectangle 类的特性 foreach (Object attributes in type.GetCustomAttributes(false)) { DeBugInfo dbi = (DeBugInfo)attributes; if (null != dbi) { Console.WriteLine("Bug no: {0}", dbi.BugNo); Console.WriteLine("Developer: {0}", dbi.Developer); Console.WriteLine("Last Reviewed: {0}", dbi.LastReview); Console.WriteLine("Remarks: {0}", dbi.Message); } } // 遍历方法特性 foreach (MethodInfo m in type.GetMethods()) { foreach (Attribute a in m.GetCustomAttributes(true)) { DeBugInfo dbi = (DeBugInfo)a; if (null != dbi) { Console.WriteLine("Bug no: {0}, for Method: {1}", dbi.BugNo, m.Name); Console.WriteLine("Developer: {0}", dbi.Developer); Console.WriteLine("Last Reviewed: {0}", dbi.LastReview); Console.WriteLine("Remarks: {0}", dbi.Message); } } } Console.ReadLine(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
Length: 4.5 Width: 7.5 Area: 33.75 Bug No: 49 Developer: Nuha Ali Last Reviewed: 10/10/2012 Remarks: Unused variable Bug No: 45 Developer: Zara Ali Last Reviewed: 12/8/2012 Remarks: Return type mismatch Bug No: 55, for Method: GetArea Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks: Return type mismatch Bug No: 56, for Method: Display Developer: Zara Ali Last Reviewed: 19/10/2012 Remarks:
属性(Property) 是类(class)、结构(structure)和接口(interface)的命名(named)成员。类或结构中的成员变量或方法称为 域(Field)。属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。它们使用 访问器(accessors) 让私有域的值可被读写或操作。
属性(Property)不会确定存储位置。相反,它们具有可读写或计算它们值的 访问器(accessors)。
例如,有一个名为 Student 的类,带有 age、name 和 code 的私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。
属性(Property)的**访问器(accessor)**包含有助于获取(读取或计算)或设置(写入)属性的可执行语句。访问器(accessor)声明可包含一个 get 访问器、一个 set 访问器,或者同时包含二者。
using System; namespace runoob { class Student { private string code = "N.A"; private string name = "not known"; private int age = 0; // 声明类型为 string 的 Code 属性 public string Code { get { return code; } set { code = value; } } // 声明类型为 string 的 Name 属性 public string Name { get { return name; } set { name = value; } } // 声明类型为 int 的 Age 属性 public int Age { get { return age; } set { age = value; } } public override string ToString() { return "Code = " + Code +", Name = " + Name + ", Age = " + Age; } } class ExampleDemo { public static void Main() { // 创建一个新的 Student 对象 Student s = new Student(); // 设置 student 的 code、name 和 age s.Code = "001"; s.Name = "Zara"; s.Age = 9; Console.WriteLine("Student Info: {0}", s); // 增加年龄 s.Age += 1; Console.WriteLine("Student Info: {0}", s); Console.ReadKey(); } } } //运行结果: Student Info: Code = 001, Name = Zara, Age = 9 Student Info: Code = 001, Name = Zara, Age = 10
抽象属性(Abstract Properties):
抽象类可拥有抽象属性,这些属性应在派生类中被实现。
using System; namespace runoob { public abstract class Person { public abstract string Name { get; set; } public abstract int Age { get; set; } } class Student : Person { private string code = "N.A"; private string name = "N.A"; private int age = 0; // 声明类型为 string 的 Code 属性 public string Code { get { return code; } set { code = value; } } // 声明类型为 string 的 Name 属性 public override string Name { get { return name; } set { name = value; } } // 声明类型为 int 的 Age 属性 public override int Age { get { return age; } set { age = value; } } public override string ToString() { return "Code = " + Code +", Name = " + Name + ", Age = " + Age; } } class ExampleDemo { public static void Main() { // 创建一个新的 Student 对象 Student s = new Student(); // 设置 student 的 code、name 和 age s.Code = "001"; s.Name = "Zara"; s.Age = 9; Console.WriteLine("Student Info:- {0}", s); // 增加年龄 s.Age += 1; Console.WriteLine("Student Info:- {0}", s); Console.ReadKey(); } } } //运行结果: Student Info: Code = 001, Name = Zara, Age = 9 Student Info: Code = 001, Name = Zara, Age = 10
索引器(Indexer) 允许一个对象可以像数组一样使用下标的方式来访问。
为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。可以使用数组访问运算符 [ ] 来访问该类的的成员。
一维索引器的语法如下:
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}
索引器(Indexer)的用途:
索引器的行为的声明在某种程度上类似于属性(property)。
就像属性(property),您可使用 get 和 set 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。换句话说,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。
定义一个属性(property)包括提供属性名称。索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例。
using System; namespace IndexerApplication { class IndexedNames { private string[] namelist = new string[size]; static public int size = 10; public IndexedNames() {//在下面 main 方法中 new 的时候,运行此构造方法,所以目前namelist中是10个"N. A." for (int i = 0; i < size; i++) namelist[i] = "N. A."; } public string this[int index]//根据 index 索引值 获取内容 { get { string tmp; if( index >= 0 && index <= size-1 ) { tmp = namelist[index]; } else { tmp = ""; } return ( tmp ); } set { if( index >= 0 && index <= size-1 ) { namelist[index] = value;//传入的 value 如 Zara } } } //重载索引器(Indexer) //索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。 //没有必要让索引器必须是整型的。C# 允许索引器可以是其他类型,例如,字符串类型。 public int this[string name] { get { int index = 0; while(index < size) { if (namelist[index] == name) { return index; } index++; } return index; } } static void Main(string[] args) { IndexedNames names = new IndexedNames(); //将原本是10个"N. A."的namelist 的前七个进行修改 names[0] = "Zara"; names[1] = "Riz"; names[2] = "Nuha"; names[3] = "Asif"; names[4] = "Davinder"; names[5] = "Sunil"; names[6] = "Rubic"; for ( int i = 0; i < IndexedNames.size; i++ ) { Console.WriteLine(names[i]); } // 使用带有 string 参数的第二个索引器 Console.WriteLine(names["Nuha"]); Console.ReadKey(); } } }
运行结果:
Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.
2
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。
⚠️ 委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。
引用可在运行时被改变。
委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
1、声明委托(Delegate)
委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。
//例如,假设有一个委托:
public delegate int MyDelegate (string s);
上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。
//声明委托的语法如下:
delegate <return type> <delegate-name> <parameter list>
2、实例化委托(Delegate)
一旦声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。例如:
using System; delegate int NumberChanger(int n); namespace DelegateAppl { 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(); } } } //结果: Value of Num: 35 Value of Num: 175
委托的多播(Multicasting of a Delegate)
委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。“-” 运算符可用于从合并的委托中移除组件委托。
使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。
using System; delegate int NumberChanger(int n); namespace DelegateAppl { 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()); Console.ReadKey(); } } } //结果: Value of Num: 75
委托(Delegate)的用途
委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。
我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件:
using System; using System.IO; namespace DelegateAppl { 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(); } } } //结果: The String is: Hello World
事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。
C# 中使用事件机制实现线程间的通信。
通过事件使用委托
事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
1、声明事件(Event)
在类的内部声明事件,首先必须声明该事件的委托类型。例如:
public delegate void BoilerLogHandler(string status);
然后,声明事件本身,使用 event 关键字:
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件,该事件在生成的时候会调用委托。
using System; namespace SimpleEvent { using System; /***********发布器类***********/ public class EventTest { private int value; public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum; protected virtual void OnNumChanged() { if ( ChangeNum != null ) { ChangeNum(); /* 事件被触发 */ }else { Console.WriteLine( "event not fire" ); Console.ReadKey(); /* 回车继续 */ } } public EventTest() { int n = 5; SetValue( n ); } public void SetValue( int n ) { if ( value != n ) { value = n; OnNumChanged(); } } } /***********订阅器类***********/ public class subscribEvent { public void printf() { Console.WriteLine( "event fire" ); Console.ReadKey(); /* 回车继续 */ } } /***********触发***********/ public class MainClass { public static void Main() { EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */ subscribEvent v = new subscribEvent(); /* 实例化对象 */ e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */ e.SetValue( 7 ); e.SetValue( 11 ); } } } //结果: event not fire event fire event fire
一个简单的用于热水锅炉系统故障排除的应用程序。
当维修工程师检查锅炉时,锅炉的温度和压力会随着维修工程师的备注自动记录到日志文件中。
using System; using System.IO; namespace BoilerEventAppl { // boiler 类 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 LogProcess() { string remarks = "O. K"; Boiler b = new Boiler(100, 12); int t = b.getTemp(); int p = b.getPressure(); if(t > 150 || t < 80 || 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); }//end of Logger static void Main(string[] args) { BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt"); DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent(); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger); boilerEvent.LogProcess(); Console.ReadLine(); filelog.Close(); }//end of main }//end of RecordBoilerInfo } //结果: Logging info: Temperature 100 Pressure 12 Message: O. K
集合(Collection)类是专门用于数据存储和检索的类。
这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。大多数集合类实现了相同的接口。
集合(Collection)类服务于不同的目的,如为元素动态分配内存,基于索引访问列表项等等。这些类创建 Object 类的对象的集合。在 C# 中,Object 类是所有数据类型的基类。
泛型(Generic) 允许延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许编写一个可以与任何数据类型一起工作的类或方法。
可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。
0、泛型(Generic)的特性
使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:
1、泛型类
using System; using System.Collections.Generic; namespace GenericApplication { public class MyGenericArray<T> { private T[] array; public MyGenericArray(int size) { array = new T[size + 1]; } public T getItem(int index) { return array[index]; } public void setItem(int index, T value) { array[index] = value; } } class Tester { static void Main(string[] args) { // 声明一个整型数组 MyGenericArray<int> intArray = new MyGenericArray<int>(5); // 设置值 for (int c = 0; c < 5; c++) { intArray.setItem(c, c*5); } // 获取值 for (int c = 0; c < 5; c++) { Console.Write(intArray.getItem(c) + " "); } Console.WriteLine(); // 声明一个字符数组 MyGenericArray<char> charArray = new MyGenericArray<char>(5); // 设置值 for (int c = 0; c < 5; c++) { charArray.setItem(c, (char)(c+97)); } // 获取值 for (int c = 0; c < 5; c++) { Console.Write(charArray.getItem(c) + " "); } Console.WriteLine(); Console.ReadKey(); } } } //结果: 0 5 10 15 20 a b c d e
2、泛型(Generic)方法
在上面的实例中,我们已经使用了泛型类,我们可以通过类型参数声明泛型方法。
using System; using System.Collections.Generic; namespace GenericMethodAppl { class Program { static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } static void Main(string[] args) { int a, b; char c, d; a = 10; b = 20; c = 'I'; d = 'V'; // 在交换之前显示值 Console.WriteLine("Int values before calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values before calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); // 调用 swap Swap<int>(ref a, ref b); Swap<char>(ref c, ref d); // 在交换之后显示值 Console.WriteLine("Int values after calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values after calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); Console.ReadKey(); } } } //结果: Int values before calling swap: a = 10, b = 20 Char values before calling swap: c = I, d = V Int values after calling swap: a = 20, b = 10 Char values after calling swap: c = V, d = I
3、泛型(Generic)委托
您可以通过类型参数定义泛型委托。例如:
delegate T NumberChanger<T>(T n);
实例:
using System; using System.Collections.Generic; delegate T NumberChanger<T>(T n); namespace GenericDelegateAppl { 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<int> nc1 = new NumberChanger<int>(AddNum); NumberChanger<int> nc2 = new NumberChanger<int>(MultNum); // 使用委托对象调用方法 nc1(25); Console.WriteLine("Value of Num: {0}", getNum()); nc2(5); Console.WriteLine("Value of Num: {0}", getNum()); Console.ReadKey(); } } } //结果: Value of Num: 35 Value of Num: 175
委托是用于引用与其具有相同标签的方法。换句话说,可以使用委托对象调用可由委托引用的方法。
匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。
在匿名方法中不需要指定返回类型,它是从方法主体内的 return 语句推断的。
编写匿名方法的语法:
匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:
delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
代码块 Console.WriteLine("Anonymous Method: {0}", x);
是匿名方法的主体。
委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。
注:匿名方法的主体后面需要一个 ;。
例如:nc(10);
using System; delegate void NumberChanger(int n); namespace DelegateAppl { class TestDelegate { static int num = 10; public static void AddNum(int p) { num += p; Console.WriteLine("Named Method: {0}", num); } public static void MultNum(int q) { num *= q; Console.WriteLine("Named Method: {0}", num); } static void Main(string[] args) { // 使用匿名方法创建委托实例 NumberChanger nc = delegate(int x) { Console.WriteLine("Anonymous Method: {0}", x); }; // 使用匿名方法调用委托 nc(10); // 使用命名方法实例化委托 nc = new NumberChanger(AddNum); // 使用命名方法调用委托 nc(5); // 使用另一个命名方法实例化委托 nc = new NumberChanger(MultNum); // 使用命名方法调用委托 nc(2); Console.ReadKey(); } } } //结果: Anonymous Method: 10 Named Method: 15 Named Method: 30
当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量。 不安全代码 或非托管代码是指使用了 指针 变量的代码块。
指针变量:
指针 是值为另一个变量的地址的变量,即,内存位置的直接地址。就像其他变量或常量,您必须在使用指针存储其他变量地址之前声明指针。
指针变量声明的一般形式为:
type* var-name;
using System; namespace UnsafeCodeApplication { class Program { static unsafe void Main(string[] args) { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} ", var); Console.WriteLine("Address is: {0}", (int)p); Console.ReadKey(); } } } //结果: Data is: 20 Address is: 99215364
也可以不用声明整个方法作为不安全代码,只需要声明方法的一部分作为不安全代码。
1、使用 ToString() 方法检索存储在指针变量所引用位置的数据。
using System; namespace UnsafeCodeApplication { class Program { public static void Main() { unsafe { int var = 20; int* p = &var; Console.WriteLine("Data is: {0} " , var); Console.WriteLine("Data is: {0} " , p->ToString()); Console.WriteLine("Address is: {0} " , (int)p); } Console.ReadKey(); } } } //结果: Data is: 20 Data is: 20 Address is: 77128984
2、传递指针作为方法的参数
using System; namespace UnsafeCodeApplication { class TestPointer { public unsafe void swap(int* p, int *q) { int temp = *p; *p = *q; *q = temp; } public unsafe static void Main() { TestPointer p = new TestPointer(); int var1 = 10; int var2 = 20; int* x = &var1; int* y = &var2; Console.WriteLine("Before Swap: var1:{0}, var2: {1}", var1, var2); p.swap(x, y); Console.WriteLine("After Swap: var1:{0}, var2: {1}", var1, var2); Console.ReadKey(); } } } //结果: Before Swap: var1: 10, var2: 20 After Swap: var1: 20, var2: 10
3、使用指针访问数组元素
在 C# 中,数组名称和一个指向与数组数据具有相同数据类型的指针是不同的变量类型。例如,int *p 和 int[] p 是不同的类型。
可以增加指针变量 p,因为它在内存中不是固定的,但是数组地址在内存中是固定的,所以不能增加数组 p。
因此,如果需要使用指针变量访问数组数据,可以像我们通常在 C 或 C++ 中所做的那样,使用 fixed 关键字来固定指针。
using System; namespace UnsafeCodeApplication { class TestPointer { public unsafe static void Main() { int[] list = {10, 100, 200}; fixed(int *ptr = list) /* 显示指针中数组地址 */ for ( int i = 0; i < 3; i++) { Console.WriteLine("Address of list[{0}]={1}",i,(int)(ptr + i)); Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i)); } Console.ReadKey(); } } } //结果: Address of list[0] = 31627168 Value of list[0] = 10 Address of list[1] = 31627172 Value of list[1] = 100 Address of list[2] = 31627176 Value of list[2] = 200
4、编译不安全代码
为了编译不安全代码,您必须切换到命令行编译器指定 /unsafe 命令行。
例如,为了编译包含不安全代码的名为 prog1.cs 的程序,需在命令行中输入命令:
csc /unsafe prog1.cs
如果您使用的是 Visual Studio IDE,那么您需要在项目属性中启用不安全代码。
步骤如下:
线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果应用程序涉及到复杂的和耗时的操作,那么设置不同的线程执行路径往往是有益的,每个线程执行特定的工作。
线程是 轻量级进程 。
一个使用线程的常见实例是现代操作系统中并行编程的实现。使用线程节省了 CPU 周期的浪费,同时提高了应用程序的效率。
到目前为止我们编写的程序是一个单线程作为应用程序的运行实例的单一的过程运行的。但是,这样子应用程序同时只能执行一个任务。为了同时执行多个任务,它可以被划分为更小的线程。
线程生命周期:
线程生命周期开始于 System.Threading.Thread 类的对象被创建时,结束于线程被终止或完成执行时。
下面列出了线程生命周期中的各种状态:
实际只是进行一个复审,但也记录一下哈哈哈哈哈
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace _04字符替换 { class Program { static void Main(string[] args) { #region 字符串替换 /* 使用正则替换字符串 */ string msg = "你aaa好aa哈哈a"; //重点是这里 msg = Regex.Replace(msg, @"a+","A"); Console.WriteLine(msg); /* 替换中使用提取组 在替换中也是可以使用提取组的 $表示提取组 $1表示组1 (引用正则表达式提取的字符串) */ string msg2 = "Hello 'welcome' to 'china'"; msg2 = Regex.Replace(msg2, "'(.+?)'", "[$1]"); Console.WriteLine(msg2); /* 隐藏手机号码 */ string msg3 = "文天祥12345678911"; msg3 = Regex.Replace(msg3, "([0-9]{4})[0-9]{4}([0-9]{3})", "$1****$2"); Console.WriteLine(msg3); /* 隐藏邮箱名 */ string msg4 = "123456789@qq.com"; msg4 = Regex.Replace(msg4, "(.+?)@", "*****"); Console.WriteLine(msg4); #endregion Console.ReadKey(); } } }
replace 可以使用在xss防御上
另外也搜到了过滤器相关文章
Split参数StringSplitOptions.RemoveEmptyEntries的使用(去除空数据):https://blog.csdn.net/u010178308/article/details/76847915
string test = "程$晓$";
使用:
string[] temp = test.Split(new string[] { "$" }, StringSplitOptions.RemoveEmptyEntries);
输出结果:数组长度为2 temp[0]=“程” temp[1]=“晓”;
使用:
string[] temp = test.Split(new string[] { "$" }, StringSplitOptions.None);
或string[] temp = test.Split('$');
输出结果:数组长度为3 temp[0]=“程” temp[1]=“晓” temp[2]=“”;
原文链接:https://blog.csdn.net/lishiyuzuji/article/details/7087214
时间换算:
1秒=1000毫秒;
1毫秒=1000微秒;
1微秒=1纳秒
而1毫秒=10000ticks;所以1ticks=100纳秒=0.1微秒
ticks这个属性值是指从0001年1月1日12:00:00开始到此时的以ticks为单位的时间,就是以ticks表示的时间的间隔数。
使用DateTime.Now.Ticks返回的是一个long型的数值。
designer.cs:是窗体设计器生成的代码文件,作用是对窗体上的控件做初始化工作(在函数InitializeComponent()中)VS 2003以前都把这部分代码放到窗体的cs文件中,由于这部分代码一般不用手工修改,在VS 2005以后把它单独分离出来形成一个designer.cs文件与窗体对应。这样cs文件中剩下的代码都是与程序功能相关性较高的代码利于维护。
.sln:解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需的信息。
.csproj:项目文件,创建应用程序所需的引用、数据连接、文件夹和文件的信息。
.aspx:Web 窗体页由两部分组成:视觉元素(HTML、服务器控件和静态文本)和该页的编程逻辑。Visual Studio 将这两个组成部分分别存储在一个单独的文件中。视觉元素在.aspx 文件中创建。
.ascx:ASP.NET 的用户控件(也叫做“pagelets”),是作为一种封装了特定功能和行为(这两者要被用在Web应用程序的各种页面上)的Web页面被开发的。一个用 户控件包含了HTML、代码和其他Web或者用户控件的组合,并在Web服务器上以自己的文件格式保存,其扩展名是*.ascx。ASP.NET里的缺省 配置并不允许Web客户端通过URL来访问这些文件,但是这个网站的其他页面可以集成这些文件里所包含的功能。
.aspx.cs:Web 窗体页的编程逻辑位于一个单独的类文件中,该文件称作代码隐藏类文件(.aspx.cs)。
.cs: 类模块代码文件。业务逻辑处理层的代码。
.asax:Global.asax 文件(也叫做 ASP.NET 应用程序文件)是一个可选的文件,该文件包含响应 ASP.NET 或 HTTP 模块引发的应用程序级别事件的代码。
.config:Web.config 文件向它们所在的目录和所有子目录提供配置信息。
.aspx.resx/.resx:资源文件,资源是在逻辑上由应用程序部署的任何非可执行数据。通过在资源文件中存储数据,无需重新编译整个应用程序即可更改数据。
.XSD:XML schema的一种.从DTD,XDR发展到XSD
.pdb:PDB(程序数据库)文件保持着调试和项目状态信息,从而可以对程序的调试配置进行增量链接。
.suo:解决方案用户选项,记录所有将与解决方案建立关联的选项,以便在每次打开时,它都包含您所做的自定义设置。
.asmx:asmx 文件包含 WebService 处理指令,并用作 XML Web services 的可寻址入口点
.vsdisco(项目发现)文件 基于 XML 的文件,它包含为 Web 服务提供发现信息的资源的链接 (URL)。
.htc:一个HTML文件,包含脚本和定义组件的一系列HTC特定元素.htc提供在脚本中implement组件的机制
https://blog.csdn.net/weixin_43942765/article/details/121938973
1、创建新项目【ASP.NET Core Web 应用程序】
创建后的样子:
2、配置Startup.cs
1.添加服务器端缓存
2.使用服务器端缓存
3.修改启动项为Home控制器下的Home视图
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Supply_System { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddResponseCaching();//1.添加服务器端缓存 services.AddControllersWithViews(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseResponseCaching();//2.使用服务器端缓存 app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Home}/{id?}");//3.修改启动项为Home控制器下的Home视图 }); } } } //ps:我的案例里没有第 1,2 步服务器缓存那些事耶
2、在Models文件夹下添加两个实体类
1.SupplyDemandsViewModel
2.TypeViewModel
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Supply_System.Models { public class SupplyDemandsViewModel { public int Id { get; set; } public string SupplyDemandTitle { get; set; } public string SupplyDemandDetail { get; set; } public string CreateTime { get; set; } public int CreateUserId { get; set; } public int TypeId { get; set; } public string TypeName { get; set; } public bool IsRecommend { get; set; } public bool IsDel { get; set; } public static List<SupplyDemandsViewModel> ListAll() { List<SupplyDemandsViewModel> supplyDemands = new List<SupplyDemandsViewModel>(); for (int i = 0; i < 8; i++) { bool IsRecommend = false; if (i > 4) { IsRecommend = true; } supplyDemands.Add(new SupplyDemandsViewModel { Id = 1, SupplyDemandTitle = "v-if", SupplyDemandDetail = "", CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), CreateUserId = 1, TypeId = 1, TypeName = "Vue控件", IsRecommend = IsRecommend, IsDel = false }); } for (int i = 0; i < 8; i++) { bool IsRecommend = false; if (i > 4) { IsRecommend = true; } supplyDemands.Add(new SupplyDemandsViewModel { Id = 1, SupplyDemandTitle = "vm", SupplyDemandDetail = "", CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), CreateUserId = 1, TypeId = 2, TypeName = "Vue语法", IsRecommend = IsRecommend, IsDel = false }); } for (int i = 0; i < 8; i++) { bool IsRecommend = false; if (i > 4) { IsRecommend = true; } supplyDemands.Add(new SupplyDemandsViewModel { Id = 1, SupplyDemandTitle = "商城", SupplyDemandDetail = "", CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), CreateUserId = 1, TypeId = 3, TypeName = "Vue实战", IsRecommend = IsRecommend, IsDel = false }); } return supplyDemands; } } } using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Supply_System.Models { public class TypeViewModel { public int Id { get; set; } public string TypeName { get; set; } public string Url { get; set; } public static List<TypeViewModel> ListAll() { List<TypeViewModel> navViewNodels = new List<TypeViewModel>(); navViewNodels.Add(new TypeViewModel { Id = 1, TypeName = "Vue控件", Url = "" }); navViewNodels.Add(new TypeViewModel { Id = 2, TypeName = "Vue语法", Url = "" }); navViewNodels.Add(new TypeViewModel { Id = 3, TypeName = "Vue实战", Url = "" }); return navViewNodels; } } }
3、修改HomeController控制器
1.添加浏览器端缓存
2.加载栏目数据
3.加载栏目对应的内容数据
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Supply_System.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; namespace Supply_System.Controllers { public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } public IActionResult Index() { return View(); } [ResponseCache(Duration = 600)]/*1.浏览器端缓存*/ public IActionResult Home() { return View(); } [ResponseCache(Duration = 600)] public IActionResult GetNavs()/*2.加载栏目数据*/ { return new JsonResult(TypeViewModel.ListAll()); } [ResponseCache(Duration = 600)] public IActionResult GetSuppys ()/*3.加载栏目对应的内容数据*/ { return new JsonResult(SupplyDemandsViewModel.ListAll()); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }
页面啥的,懒得写了,看原文吧
https://blog.csdn.net/weixin_30315435/article/details/98642956
仓储(Respository)是存在于工作单元和数据库之间单独分离出来的一层,是对数据访问的封装。其优点:
1)业务层不需要知道它的具体实现,达到了分离关注点。
2)提高了对数据库访问的维护,对于仓储的改变并不会改变业务的逻辑,数据库可以用Sql Server(该系列博客使用)、MySql等。
https://blog.csdn.net/MaNong_girl/article/details/120485741
1、JSON.stringify() 从一个对象中解析出字符串
JSON.stringify({"a":"1","b":"2"})
结果是:"{"a":"1","b":"2"}"
2、JSON.parse() 从一个字符串中解析出JSON对象
var str = '{"a":"1","b":"2"}';
JSON.parse(str);
结果是:Object{a:"1",b:"2"}
ps:我的案例基本上是这样式的:xxx = JSON.parse(JSON.stringify(data));
所以说是一个对象变成了字符串,又变成了json对象嘛hhhh
GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成GUID的API。生成算法很有意思,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。GUID的唯一缺陷在于生成的结果串会比较大。”
一个GUID为一个128位的整数(16字节),在使用唯一标识符的情况下,你可以在所有计算机和网络之间使用这一整数。
GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。例如:337c7f2b-7a34-4f50-9141-bab9e6478cc8 即为有效的 GUID 值。
世界上(Koffer注:应该是地球上)的任何两台计算机都不会生成重复的 GUID 值。GUID 主要用于在拥有多个节点、多台计算机的网络或系统中,分配必须具有唯一性的标识符。
在 Windows 平台上,GUID 应用非常广泛:注册表、类及接口标识、数据库、甚至自动生成的机器名、目录名等。
**.NET中使用GUID **
GUID 在 .NET 中使用非常广泛,而且 .NET Framework 提供了专门 Guid 基础结构。
Guid 结构的常用法包括:
using System; namespace TestGuid { class Program { static void Main(string[] args) { // format 参数可以是“N”、“D”、“B”、“P”或“X”。如果 format为 null 或空字符串 (""),则使用“D”。 string uuid = Guid.NewGuid().ToString(); string uuid2 = Guid.NewGuid().ToString("N"); string uuid3 = Guid.NewGuid().ToString("D"); string uuid4 = Guid.NewGuid().ToString("B"); string uuid5 = Guid.NewGuid().ToString("P"); string uuid6 = Guid.NewGuid().ToString("x"); Console.WriteLine(uuid); // 64bc127b-4339-4121-b626-cdb8dc58f517 Console.WriteLine(uuid2); // ebb60bc3b1554a4b809141212428a7d8 Console.WriteLine(uuid3); // 00155eb3-3a7e-4097-b404-dc00a035ddad Console.WriteLine(uuid4); // {a9101c4b-bb1e-4491-b40b-8d7872c9c7df} Console.WriteLine(uuid5); // (de66cc8b-32eb-466d-81ef-ecb87e2e1431) Console.WriteLine(uuid6); // {0x7cbf8638,0x39cb,0x495d,{0x80,0x34,0x2d,0xbe,0x75,0xb6,0xad,0x87}} Console.ReadLine(); } } }
说明符 | 说明 | 格式 |
---|---|---|
N | 32 十六进制数字 | 00000000000000000000000000000000 |
D | 用连字符分隔的 32 个十六进制数字 | 00000000-0000-0000-0000-000000000000 |
B | 用连字符分隔的 32 个十六进制数字,用大括号括起来 | {00000000-0000-0000-0000-000000000000} |
P | 用连字符分隔的 32 个十六进制数字,括在括号中 | (00000000-0000-0000-0000-000000000000) |
X | 四个十六进制值括在大括号中,其中第四个值是八个十六进制值的子集,也括在大括号中 | {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} |
1、Guid.Parse
https://docs.microsoft.com/zh-cn/dotnet/api/system.guid.parse?view=net-6.0
Parse(ReadOnlySpan) | 将表示 GUID 的只读字符范围转换为等效的 Guid 结构。 |
---|---|
Parse(String) | 将 GUID 的字符串表示形式转换为等效的 Guid 结构。 |
2、Guid.Empty
Guid 结构的只读实例,其值均为零。
可以将 GUID 与字段的值 Guid.Empty 进行比较,以确定 GUID 是否为非零。 以下示例使用 Equality运算符比较两个 GUID 值,以确定 Guid.Empty 它们是否只包含零。
// Create a GUID and determine whether it consists of all zeros. Guid guid1 = Guid.NewGuid(); Console.WriteLine(guid1); Console.WriteLine($"Empty: {guid1 == Guid.Empty}\n"); // Create a GUID with all zeros and compare it to Empty. var bytes = new Byte[16]; var guid2 = new Guid(bytes); Console.WriteLine(guid2); Console.WriteLine($"Empty: {guid2 == Guid.Empty}"); // The example displays output like the following: // 11c43ee8-b9d3-4e51-b73f-bd9dda66e29c // Empty: False // // 00000000-0000-0000-0000-000000000000 // Empty: True
[NonAction]:该特性用于指示控制器方法不是操作方法。
NonAction表示它不是一个真正的Action,而是一个普通方法。
默认情况下,MVC 框架将 controller 类的所有公共方法都视为操作方法Action(浏览器输入地址即可访问)。
如果ni的 controller 类包含公共方法,并且您不希望它成为操作方法Aciton,则必须用 NonActionAttribute 特性标记该方法。
NonActionAttribute 类
表示一个特性,该特性用于指示控制器方法不是操作方法。在其他控制器中可调用该方法
[NonAction]
public string GetClassName()
{
return "hello";
}//controller 中直接调用GetClassName()
另外还有 ChildActionOnly 类
ChildActionOnly表示它只能在View中通过Html.Action或Html.RenderAction来使用,不能被 Controller 直接调用, 一般返回的是局部视图,例如更新局部页面时,在 主view 中使用 Html.Action 或 Html.RenderAction 来调用
[ChildActionOnly]
public ActionResult GetClassName(int id)
{
return Content("体育");
}//view中直接调用@Html.Action("GetClassName", new { id = 1 })
EF 框架 是微软的.NET中ORM(对象关系映射)框架。
在EF框架中把数据库中的关系表对应到了程序中的实体类,把数据表中的字段对应成了实体类中的属性,这就是对象关系映射
原文链接:https://blog.csdn.net/lnazj/article/details/79062652
架构组件:
EDM(实体数据模型): EDM由三个主要部分组成 - 概念模型,映射和存储模型。
Conceptual Model:概念模型包含模型类及其关系。这将独立于您的数据库表设计。
Storage Model:存储模型是包括表,视图,存储过程及其关系和密钥的数据库设计模型。
Mapping:映射由有关概念模型如何映射到存储模型的信息组成。
LINQ to Entities: LINQ to Entities是一种用于针对对象模型编写查询的查询语言。它返回在概念模型中定义的实体。你可以在这里使用你的LINQ技能。
Entity SQL:实体SQL是另一种查询语言(仅适用于EF 6),就像LINQ to Entities一样。然而,这比L2E稍微难一些,开发者需要单独学习。
ObjectServices:对象服务是访问数据库中的数据并将其返回的主要入口点。对象服务负责实现,这是将从实体客户端数据提供者(下一层)返回的数据转换为实体对象结构的过程。
Entity Client Data Provider:此层的主要职责是将LINQ-to-Entities或实体SQL查询转换为底层数据库可以理解的SQL查询。它与ADO.Net数据提供者通信,而ADO.Net数据提供者又从数据库发送或检索数据。
ADO.Net Data Provider:该层使用标准的ADO.Net与数据库进行通信。
功能:
https://blog.csdn.net/csdn2990/article/details/121281103
一、先安装对应的数据库映射工具。一般对应的数据库映射工具包含EntityFrameworkCore的依赖。
一般EntityFrameworkCore(ORM映射工具)安装在你的数据项目工程中,这里我通过nuget安装的sqlserver的包。对应的数据和安装对应的包就行。包名如下:Microsoft.EntityFrameworkCore.SqlServer
然后我们建立好对应的数据Model
Model建立完成后,我们建立数据库上下文文件如下
该类需要继承DbContext。跟数据库的交互,都在这个类中进行。
其中 DbSet 的作用就是暴露Modle让他们能够在该类中正常使用。DbSet中的泛型相当于数据库中的表。
OnConfiguring可以做一些相关的配置,比如链接字符串。
二、安装进行数据迁移的命令工具。
Model建立好之后,我们可以安装对应的工具,进行数据迁移。安装如下包:Microsoft.EntityFrameworkCore.Tools,同样该包也是安装在你的数据工程当中。
打开包管理控制台
进行数据迁移,
先把默认项目选为你安装了包的项目
然后执行: get-help entityframework
获取所有的指令,一般用添加指令和更新指令就够了。
然后我们进行第一次数据迁移,输入add-migration Initial
的指令,其中inital为迁移的名称(会在生成的文件名中的_后面出现,也会是生成文件的类名。ps:类名可以和文件名不统一),可以随便取。
会有如同的提示,大概意思就是你的启动项没有安装 Microsoft.EntityFrameworkCore.Design
这个包,然后你去把这个包安装到你的启动项中,然后再执行一遍刚才的命令。
其中DemoContextModelSnapshot.cs它相当于是个快照,它非常重要,而且我们不能手动去修改,作用就是EFcore通过他去追踪我们Model的一个状态,比如说:我们又加了一个model,EFcore就会读取这个快照,看看他当前所追踪的快照是什么样,然后跟他以现在大model进行比较,比较出差异之后,就知道改一些什么东西了。
看下Initial这个类
这个类继承了Migration这个类,相当于使用了他的API,这Initial类中就2个方法。
执行生成数据库或者生成脚本
执行命令来生成sql脚本(一般生产环境下才会使用):Script-Migration
开发环境一般使用一下命令进行数据库更新:update-database -verbose
其中verbose是可选参数,加了之后可以看到数据库生成的详细信息。一般不加。
执行完成后可以看到如图 数据库 中生成了4张对应的表,(os:还有个Players在DBSet那里没有截图住,这里也没截住)
其中三张是我自己创建的Model,另外__EFMigrationsHistory这个表是用来做数据迁移的历史纪录的。
另外上面我们采用的方式是code first方式,还有其他方式,详情请看微软文档。EF core官网
同样如果有写好的数据库脚本,也可以通过数据库脚本去生成对应的模型。详情请看微软文档。
三、接着我们来对模型中的字段做一些相应的限制。
首先打开nuget安装system.componentModel.Annotations相应的库
然后我们进行相关的引用:using System.ComponentModel.DataAnnotations;
然后我们就可以在相应的字段中进行注释。下图设置了一些常用注释,详细注释请参考微软官网
因为做了相应的更改,所以我们可以进行一次数据迁移。执行如下命令,进行更新操作:Add-migration ChangeSomeOrioerties
执行成功后,我们可以看下执行后生成的对应文件。(再次生成那个带up,down方法的文件)
using System; using Microsoft.EntityFrameworkCore.Migrations; namespace Demo.Data.Migrations { public partial class ChangeSomeOrioerties : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterColumn<string>( name: "Name", table: "Leagues", type: "nvarchar(100)", maxLength: 100, nullable: true, oldClrType: typeof(string), oldType: "nvarchar(max)", oldNullable: true); migrationBuilder.AlterColumn<string>( name: "Country", table: "Leagues", type: "nvarchar(50)", maxLength: 50, nullable: false, defaultValue: "", oldClrType: typeof(string), oldType: "nvarchar(max)", oldNullable: true); migrationBuilder.AlterColumn<DateTime>( name: "DateOfEstablishment", table: "Clubs", type: "date", nullable: false, oldClrType: typeof(DateTime), oldType: "datetime2"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.AlterColumn<string>( name: "Name", table: "Leagues", type: "nvarchar(max)", nullable: true, oldClrType: typeof(string), oldType: "nvarchar(100)", oldMaxLength: 100, oldNullable: true); migrationBuilder.AlterColumn<string>( name: "Country", table: "Leagues", type: "nvarchar(max)", nullable: true, oldClrType: typeof(string), oldType: "nvarchar(50)", oldMaxLength: 50); migrationBuilder.AlterColumn<DateTime>( name: "DateOfEstablishment", table: "Clubs", type: "datetime2", nullable: false, oldClrType: typeof(DateTime), oldType: "date"); } } }
这个文件是如何生成出来的呢,EF Core会先和之前的 DemoContextModelSnapshot.cs 也就是 **快照 **这个类进行对比,对比后EFcore就可以算出来你又做了哪些动作,然后达到状态同步的一个效果。
然后我们执行命令对数据库进行更新操作:update-database
然后我们可以看下迁移表:
四、接着我们来对模型进行一对多,多对多,一对一的关系建立。
有如下关系:
这个文章写得不错,下面还很多内容(增删改查),看原文吧
因为和下面Dbcontext的内容有些重复就不写了,但我觉得这个文章写的更容易理解些
举个查询的小例子:
//精确查询: // 方式1 Linq方法进行查询 var leagues = context.Leagues.Where(x => x.Country == "Italy").ToList(); // 方式2 Linq语句的方式进行查询 var leagues2 = (from lg in context.Leagues where lg.Country == "Italy" select lg).ToList(); //模糊查询: //方式1: var leagues = context.Leagues.Where(x => x.Country.Contains("a")).ToList(); //方式2: var leagues1 = context.Leagues.Where(x => EF.Functions.Like(x.Country,"%a%") ).ToList();
Where之后返回的是IQueryable类型 此时只生成了对应的sql语句,而并没有执行,它会延迟加载 只有你ToList之后才会立即执行。
这里举例一下和ToList有等价效果的函数,这些函数都会使sql立即执行。
Tolist() 返回查询结果。
First() 返回第一条数据,没有数据会报错。
FirstOrDefault() 返回第一条数据 它可以有返回结果,也可以没有
Single() 符合查询条件的只能是一条数据
SingleOrDefault() 符合查询条件的只能是一条数据,或者没有数据
Last() 返回最后一条
LastOrDefault() 返回最后一条,可以为空
Count() 返回查询条数
LongCount() 返回表示序列中满足条件的元素的数量的
Min() 最小值
Max() 最大值
Sum() 计算总和
Average() 平均值
Find() 查找匹配的结果,这个不属于Linq方法,是Contex的方法,但也会立即执行
以上方法都有对应的异步方法。
----还有什么加载之类的东西,懒得写了
DbContext类是EntityFramework (简称 EF)中的一个类,可以理解为一个数据库对象的实例。在 EF中,无需手动的拼接 SQL 语句对数据库进行增删改查,而是通过 DbContext 来进行相应操作。
DbContext类是实体框架的重要组成部分,是域或实体类与数据库之间的桥梁。
DbContext是负责与数据交互作为对象的主要类。DbContext负责以下活动:
(1)EntitySet: DbContext包含映射到数据库表的所有实体的实体集(DbSet <TEntity>
)。
(2)查询(Querying): DbContext 将 LINQ-to-Entities 查询转换为SQL查询并将其发送到数据库。
(3)更改跟踪(Change Tracking):跟踪实体在从数据库查询后发生的更改。
(4)持久数据(Persisting Data):它还根据实体的状态对数据库执行插入,更新和删除操作。
(5)缓存(Caching): DbContext默认进行一级缓存。它存储在上下文类生命周期中已经被检索的实体。
(6)管理关系(Manage Relationship): DbContext还使用DB-First或Model-First方法使用CSDL,MSL和SSDL或者使用Code-First方法使用流利的API来管理关系。
(7)对象实现(Object Materialization): DbContext将原始表数据转换为实体对象。
参考2:https://blog.csdn.net/lnazj/article/details/79066192
DbContext类的方法:
**DbSet:**表示可用于创建,读取,更新和删除操作的实体集。
DbSet 常用的方法:
Add:将给定的实体添加到添加状态的上下文中。当保存更改时,添加状态中的实体将被插入到数据库中。保存更改后,对象状态将更改为“未更改”。
Remove:将给定的实体标记为已删除。保存更改后,实体将从数据库中删除。在调用此方法之前,实体必须存在于其他某个状态的上下文中。
以下是生成的excellentmcoinEntities类(派生DbContext的上下文类)的示例:
//------------------------------------------------------------------------------ // <auto-generated> // 此代码是根据模板生成的。 // // 手动更改此文件可能会导致应用程序中发生异常行为。 // 如果重新生成代码,则将覆盖对此文件的手动更改。 // </auto-generated> //------------------------------------------------------------------------------ namespace Model { using System; using System.Data.Entity; using System.Data.Entity.Infrastructure; public partial class excellentmcoinEntities : DbContext { public excellentmcoinEntities() : base("name=excellentmcoinEntities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } //DbSet表示上下文中指定类型的所有实体的集合或可从数据库中查询的指定类型的所有实体的集合 //巴拉巴拉 原博文这里写了很多个,删了些 public DbSet<t_stageconfluence> t_stageconfluence { get; set; } public DbSet<t_states> t_states { get; set; } public DbSet<t_suggesstion> t_suggesstion { get; set; } public DbSet<t_type> t_type { get; set; } public DbSet<t_user> t_user { get; set; } public DbSet<t_attributerecord> t_attributerecord { get; set; } public DbSet<t_notice> t_notice { get; set; } } }
使用:
//增 excellentmcoinEntities db = new excellentmcoinEntities(); //创建对象实体,注意,这里需要对所有属性进行赋值(除了自动增长主键外),如果不赋值,则会数据库中会被设置为NULL(注意是否可空) var user = new User { Name = "bomo", Age = 21, Gender = "male" }; db.User.Add(user); db.SaveChanges(); //删 public bool deleteUser(string UserID) { excellentmcoinEntities dbcontext = new excellentmcoinEntities(); try { string[] testUserID = UserID.Split(','); for (int i = 0; i < testUserID.Length; i++) { t_user usermodel = dbcontext.t_user.Find(testUserID[i]); dbcontext.t_user.Remove(usermodel); int flag = dbcontext.SaveChanges(); if (flag > 0) { continue; } else { return false; } } return true; } catch (Exception) { throw new Exception("删除失败"); } } //改 public bool EditUser(t_user usermodel) { excellentmcoinEntities dbcontext = new excellentmcoinEntities(); try { t_user user = dbcontext.t_user.Find(usermodel.userID); if (user == null) { return false; } user.userName = usermodel.userName; user.levelID = usermodel.levelID; user.state = usermodel.state; user.passWord = usermodel.passWord; //修改多个字段值 dbcontext.Entry<t_user>(user).State = System.Data.EntityState.Modified; dbcontext.SaveChanges(); return true; } catch (Exception) { throw new Exception("用户修改失败"); } } //查 public List<userModel> QueryAUser() { //定义了上下文实体 excellentmcoinEntities dbcontext = new excellentmcoinEntities(); var allUser = (from u in dbcontext.t_user join g in dbcontext.t_grade on u.gradeID equals g.gradeID orderby u.totalMcoin descending select new userModel() { userID = u.userID, userName = u.userName, userGrade = g.userGrade, totalMcoin = u.totalMcoin, gradeID = g.gradeID, }).ToList(); return allUser; }
DbContext通常与包含模型的根实体的DbSet 属性的派生类型一起使用。当派生类的实例被创建时,这些集合会自动初始化。
https://cloud.tencent.com/developer/ask/sof/48027
首先在实体框架代码中,当我声明实体时,我必须使用DbSet<>类型的属性。例如:
public DbSet<Product> Products { get; set; }
public DbSet<Customer> Customers { get; set; }
最近,我遇到了被声明为虚拟的DbSet<>。
public virtual DbSet<Product> Products { get; set; }
public virtual DbSet<Customer> Customers { get; set; }
有什么关系?启用了哪些EF功能?
数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称. 数据模型可以是任何数据结构或对象模型,存储模型可以是关系模型、XML、二进制流等。(即 内存 ----》磁盘)
使用实体框架将实体持久(保存)到数据库有两种情况:
1、连接方案(在连接的场景下保存数据)
在连接场景中,同一个上下文类(从DbContext派生)实例用于检索和保存实体,并且跟踪所有的实体。
下图是连接场景下CUD操作
根据上图,实体框架为调用方法EntityState时添加,修改或删除的实体构建并执行INSERT,UPDATE或DELETE语句 DbContext.SaveChanges()。在连接场景中,一个实例DbContext跟踪所有实体,所以EntityState无论何时创建,修改或删除实体,它都会自动设置每个实体的适当值。
插入数据
使用DbSet.Add方法将新实体添加到上下文(实例DbContext),当您调用该SaveChanges()方法时,该实例将在数据库中插入新记录。
using(var context = new SchoolDBEntities())
{
var std = new Student()
{
FirstName = “Bill”,
LastName = “门”
};
context.Students.Add(std);
context.SaveChanges();
}
在上面的例子中,使用EntityState context.Students.Add(std)将一个新创建的Student实体实例添加到上下文中Added。
该context.SaveChanges()方法构建并执行以下INSERT语句到数据库。(看到了使用了参数化查询)
exec sp_executesql N'INSERT [dbo].[Students]([FirstName], [LastName])
VALUES (@0, @1)
SELECT [StudentId]
FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()',N
''@0 nvarchar(max) ,@1 nvarchar(max) ',@0=N'Bill',@1=N'Gates'
go
更新数据
在连接场景中,EF API跟踪使用上下文检索的所有实体。因此,当您编辑实体数据时,EF将自动标记EntityState为Modified,当您调用该SaveChanges()方法时,它会在数据库中建立并执行下面的Update语句,会导致数据库中更新的语句。
using (var context = new SchoolDBEntities())
{
var std = context.Students.First<Student>();
std.FirstName = "Steve";
context.SaveChanges();
}
在上面的例子中,我们使用数据库检索第一个学生context.Students.First()。一旦我们修改了FirstName,上下文将它设置EntityState为Modified因为在DbContext实例(上下文)范围内执行的修改。
所以,当我们调用这个SaveChanges()方法时,它会在数据库中建立并执行下面的Update语句。
exec sp_executesql N'UPDATE [dbo].[Students]
SET [FirstName] = @0
WHERE ([StudentId] = @1)',
N'@0 nvarchar(max) ,@1 int',@0=N'Steve',@1=2
Go
删除数据
using (var context = new SchoolDBEntities())
{
var std = context.Students.First<Student>();
context.Students.Remove(std);
context.SaveChanges();
}
context.Students.Remove(std)将std实体对象标记为Deleted。因此,当执行context.SaveChanges()时,EF将在数据库中构建并执行以下DELETE语句。
exec sp_executesql N'DELETE [dbo].[Students]
WHERE ([StudentId] = @0)',N'@0 int',@0=1
Go
总结:
通过让DbSet(实体集) 来执行CUD (添加,更新,删除) 操作实体,然后会更改EntityState 属性为(Added,Modified,Deleted),然后当上下文context 执行SaveChanges方法时,会根据EntityState 属性值来操纵数据库,执行insert,update或者delete 操作。OnsaveChange方法封装了对数据库的增删改查操作
2、断开方案
WebClient读取网络数据: https://blog.csdn.net/weixin_30512043/article/details/97790366
WebClient.DownloadData (String) 以 Byte 数组形式通过指定的 URI 下载资源
WebClient.DownloadData (Uri) 以 Byte 数组形式通过指定的 URI 下载资源
WebClient.DownloadFile (String, String) 将具有指定 URI 的资源下载到本地文件
WebClient.DownloadFile (Uri, String) 将具有指定 URI 的资源下载到本地文件
WebClient.DownloadString (String) 以 String 形式下载指定的资源
WebClient.DownloadString (Uri) 以 Uri 形式下载指定的资源
以上这些方法,均有xxxAsync方法与之对应,目的是使得xxx方法不会阻止调用线程。如WebClient.DownloadFileAsync (Uri, String)方法,就是异步下。
其中,WebClient.DownloadData (Uri) 方法返回Byte数组,容易出现中文乱码问题,因为中文是双字节数据,而返回byte类型,因此这时候,中文便容易出现乱码,解决方案可能是转换为unicode什么的吧。
建议用 WebClient.DownloadString(Uri) ,如果此时还出现乱码问题,便是字符集问题了,可以用
WebClient client = new WebClient();
string s = client.DownloadString("http://www.cnblogs.com");
s=Encoding.UTF8.GetString(Encoding.Default.GetBytes(s);
https://blog.csdn.net/qq_39569480/article/details/121770414
https://blog.csdn.net/vincent_ling/article/details/51714691
跨域配置也是在 startup.cs 文件中的ConfigureServices方法下
services.AddCors(options =>
{
// Policy 名称 CorsPolicy 是自定义的,可以自己改
options.AddPolicy("qwer", policy =>
{
// 设定允许跨域的来源,有多个的话可以用 , 隔开
string CorsUrl= Configuration.GetConnectionString("CorsOrigins");//通过注入的IConfiguration 获取appsetting.json中的自定义路径
string[] CoreArray = CorsUrl.Split(',');//appsetting.json中的配置
//policy.WithOrigins("http://localhost:8080", "http://192.168.0.86:8080","http://123.123.123.123:5555")//写死的方式,不方便
policy.WithOrigins(CoreArray)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();//这次代码审计的源码里,这个没开,就没审计这个漏洞
});
});
同时修改 startup.cs 文件中的Configure 方法
添加
app.UseCors("qwer");//必须位于UserMvc之前
思路:
express
搭建第一个服务A
(http://localhost:8000
),运行在8000
端口上;A
服务托管index.html
(用于在前端页面发送网络请求)文件;A
服务中写一个处理请求的路由,加载index.html
页面时,种下cookie
(这里种cookie
为了在请求B
服务时携带上);express
搭建第二个服务B
(http://localhost:8003
),运行在8003
端口上;A
服务托管的index.html
页面去请求B
服务,然后把cookie
传过去;总结:
request
对象中配置"withCredentials": true
;XMLHttpRequest.withCredentials属性是一个Boolean类型,它指示了是否该使用类似 cookies,authorization headers(头部授权)或者 TLS 客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。
response
的header
中配置"Access-Control-Allow-Origin", "http://localhost:8000"
;response
的header
中配置"Access-Control-Allow-Credentials", "true"
format:https://blog.csdn.net/jirigala/article/details/5829455
format并不具备防护sql注入的功能,仅仅只是拼接字符串
string userName = " lili" ;
string password = " 123456 " ;
string sqlQuery = string .Format( " SELECT * FROM Base_User WHERE UserName='{0}' AND UserPassword='{1}' " , userName, password);
https://www.jb51.net/article/45881.htm
https://blog.csdn.net/Ca_va/article/details/107973851
防护方法:
1、使用SQL参数化 @
string sqlStr="select * from [Users] where UserName=@UserName and Password=@Password"
//然后创建命令对象的代码时,修改为:
SqlCommand cmd = new SqlCommand(sqlStr,conn);
cmd.Parameters.AddWithValue("@UserName",txtUserName.Text.Trim());
cmd.Parameters.AddWithValue("@Password",txtUserPassword.Text.Trim());
安全经典JWT算法漏洞:https://blog.csdn.net/kali_Ma/article/details/121609999
Asp.NetCore3.1 WebApi 使用Jwt 授权认证使用:https://blog.csdn.net/qq_18932003/article/details/119985995
1:导入NuGet包 Microsoft.AspNetCore.Authentication.JwtBearer
2 : 配置 jwt相关信息
3:在 startUp中
public void ConfigureServices(IServiceCollection services){ #region JWT 认证 services .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { var jsonmodel = AppJsonHelper.InitJsonModel(); options.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = jsonmodel.Issuer,// Configuration["JwtSetting:Issuer"], ValidAudience = jsonmodel.Audience,// Configuration["JwtSetting:Audience"], // IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSetting:SecurityKey"])), IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jsonmodel.TockenSecrete)), // 默认允许 300s 的时间偏移量,设置为0即可 ClockSkew = TimeSpan.Zero }; }); #endregion } //注意需要放在addmvc上面 services.AddMvc(); public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseAuthentication();//身份验证 app.UseAuthorization();// 授权 }
4:使用时在Controller /action 上打上特性 [Authorize]
可以单独在Action上打上特性[Authorize] 不需要检查授权认证的话打上特性: [AllowAnonymous]
两个特性类都在如下命名空间下:using Microsoft.AspNetCore.Authorization;
5:登陆成功后端并返回生成的Token,可以在PostMan上面测试,和JWT.io官网上面来测试
6: 发送请求到后端,带上Token 如Get
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。