C#小王的笔记
何如精简循环
any(任何、一个)和all(所有)
any:list1.Where(x=> list2.Any(u=>u.id==x.id)),list1(1、2、3)、list2(1、2、2)。结果为(1、2)
因为any是根据list2里面数据去判断的而且他不会因为里面有两个2而出现的结果为(1、2、2),所以他在进行计算的时候不能用any,只能够是在.any的集合值为唯一的时候可以使用比如list1。
all::list1.Where(x=> list2.Any(u=>u.id==x.id)),list1(1、2、3)、list2(1、2、2)。结果为()
all是必须在list集合中全有也就是当list1.Add(2)的时候会返回结果为list2.
demo1
public class Test
{
public int age { get; set; }
public string name { get; set; }
public int score { get; set; }
}
static void Main(string[] args)
{
List<Test> list1 = new List<Test>();
list1.Add(new Test { score = 10, name = "001" });
list1.Add(new Test { score = 20, name = "002" });
list1.Add(new Test { score = 30, name = "003" });
list1.Add(new Test { score = 40, name = "004" });
list1.Add(new Test { score = 50, name = "005" });
list1.Add(new Test { score = 60, name = "005" });
List<Test> list2 = new List<Test>();
list2.Add(new Test { score = 10, name = "001" });
list2.Add(new Test { score = 20, name = "002" });
list2.Add(new Test { score = 30, name = "003" });
list2.Add(new Test { score = 40, name = "004" });
//list3 return 2
List<Test> list3 = list1.Where(x => list2.Any(x2 => x.score == x2.score)).ToList();
//list4 return 2
List<Test> list4 = list1.Where(x => list2.All(x2 => x.score != x2.score)).ToList();
foreach (var item in list4)
{
Console.WriteLine(item.score);
}
demo2
var Funditem = FundData.Where(u => u.Contains("tec_isstatisticscumulativescale") && u.GetAttributeValue<bool>("tec_isstatisticscumulativescale"));
var FundPartneritem = FundPartnerData.Where(u => u.Contains("tec_fundid") && Funditem.Any(x => x.GetAttributeValue<Guid>("tec_fundid") == u.GetAttributeValue<EntityReference>("tec_fundid").Id) && u.Contains("tec_commitment")).Sum(u => u.GetAttributeValue<Money>("tec_commitment").Value);
//foreach (Entity Funditem in FundData)
//{
// //判断当前基金是否统计累计规模
// if (Funditem.GetAttributeValue<bool>("tec_isstatisticscumulativescale") && Funditem.Contains("tec_isstatisticscumulativescale"))
// {
// //求出相应的认缴出资额
// foreach (Entity FundPartneritem in FundPartnerData)
// {
// if (FundPartneritem.GetAttributeValue<EntityReference>("tec_fundid") != null)
// {
// if (FundPartneritem.GetAttributeValue<EntityReference>("tec_fundid").Id == Funditem.GetAttributeValue<Guid>("tec_fundid"))
// {
// if (FundPartneritem.GetAttributeValue<Money>("tec_commitment") != null)
// {
// num += (double)FundPartneritem.GetAttributeValue<Money>("tec_commitment").Value;
// }
// }
// }
注意
这种方式只能在前面.any一方唯一的时候可用不可重复
C#托管对象和非托管对象
托管对象指的是.net可以自动进行回收的资源,主要是指托管对象在堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.net运行库在合适的时间进行回收。(手动回收GC.Collect)
非托管对象指.net不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件、窗口、网络连接、数据库连接、画刷、图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象在此方法中需要编写回收非托管对象的代码,以便垃圾回收器正确回收。(例如我们通常打开文件、图片等后需要进行Close()或者Dispose()去释放)。
本来如果按照上面做法,非托管资源也能够由垃圾回收器进行回收,但是非托管资源一般是有限的,比较宝贵的,而垃圾回收器是由CRL自动调用的,这样就无法保证及时的释放掉非托管资源,因此定义了一个Dispose()方法,让使用者能够手动的释放非托管资源。Dispose()方法释放类的托管资源和非托管资源,使用者手动调用此方法后,垃圾回收器不会对此类实例再次进行回收。Dispose()方法是由使用者调用的,在调用时,类的托管资源和非托管资源肯定都未被回收,所以可以同时回收两种资源。
Microsoft为非托管资源的回收专门定义了一个接口:IDisposable,接口中只包含一个Dispose()方法。任何包含非托管资源的类,都应该继承此接口。
在一个包含非托管资源的类中,关于资源释放的标准做法是:
(1)继承IDisposable()接口;
(2)实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);
(3)实现类析构函数,在其中释放非托管资源。
这样做的目的是为了更好的提高程序性能,避免内存泄漏,小数据量往往体现不出来,当数据量庞大时就会出现内存不足以及System.OutOfMemoryException:“Exception_WasThrown”的错误。
堆与栈
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TD9aICH5-1686218174643)(C:\Users\29310\AppData\Roaming\Typora\typora-user-images\image-20230412145252651.png)]
1、堆与栈概念介绍
堆:在c里面叫堆,在c#里面其实叫托管堆。
栈:就是堆栈,因为和堆一起叫着别扭,就简称为栈。
2、托管堆
托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。
3、内存堆栈与数据堆栈
内存堆栈:存在内存中的两个存储区(堆区,栈区)。
栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放。
堆区:存放着引用类型的对象,由CLR释放。
数据堆栈:是一种后进先出的数据结构,它是一个概念,主要是栈区。
4、堆与栈区别分析
栈通常保存着我们代码执行的步骤,如一个值类型的变量的初始化或者一个方法的声明。而堆上存放的则多是对象,数据等。我们可以把栈想象成一个接着一个叠放在一起的盒子。当我们使用的时候,每次从最顶部取走一个盒子。同样,我们的栈也是如此,当一个方法(或类型)被调用完成的时候,就从栈顶取走,接着下一个,这也就是我们常说的 “先进后出” 。堆则不然,像是一个仓库,储存着我们使用的各种对象等信息,当我们需要调用的时候,会去里面自行寻找并调用。跟栈不同的是它们被调用完毕不会立即被清理掉。
注意:栈内存无需我们管理,也不受GC管理。当栈顶元素使用完毕,立马释放。而堆则需要GC(Garbage Collection:垃圾收集器)清理。
5、堆与栈存储讲解
我们把内存分为堆空间和栈空间,区别如下:
栈空间比较小,但是读取速度快。
堆空间比较大,但是读取速度慢。
5-1、栈的深入讲解
栈(Stack)最明显的特征就是“先进后出”,本质上讲堆栈也是一种线性结构,符合线性结构的基本特点:即每个节点有且只有一个前驱节点和一个后续节点。栈把所有操作限制在"只能在线性结构的某一端"进行,而不能在中间插入或删除元素。我们把数据放入栈顶称为入栈(push), 从栈顶删除数据称为出栈(pop)。
5-2、堆的深入讲解
堆(Heap)是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除。
6、GC(Garbage Collection)垃圾收集器介绍
CLR的GC就是内存管理机制,我们写程序不需要关心内存的使用,因为这些都是CLR帮我们做了。
unicode
Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。 1990年开始研发,1994年正式公布。
static void Main(string[] args)
{
//Unicode
Console.WriteLine(Regex.Unescape("\u6211\u7231\u4f60"));
StringToUnicode("我爱你");
Console.WriteLine(UnicodeRegexToString("\u6211\u7231\u4f60"));
}
/// <summary>
/// Unicode转字符串 (正则形式)
/// </summary>
/// <param name="source">Unicode码</param>
/// <returns></returns>
static string UnicodeRegexToString(string source)
{
return new Regex(@"\\u([0-9A-F]{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled).Replace(
source, x => string.Empty + Convert.ToChar(Convert.ToUInt16(x.Result("$1"), 16)));
}
static void StringToUnicode(string source)
{
char[] cs = source.ToCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cs.Length; i++)
{
sb.AppendFormat("\\u{0:x4}", (int)cs[i]);
}
Console.WriteLine(sb.ToString());
}
Distinct和重写IEqualityComparer
我们在想对一个可枚举的对象集合进行去重操作时,一般第一个想到的就是就是Linq的Distinct方法。
先定义一个类,然后使用Distinct方法去重
class Man
{
public int Age { get; set; }
public string Name { get; set; }
public string Adress { get; set; }
public decimal Weight { get; set; }
public decimal Height { get; set; }
}
List<Man> list = new List<Man>()
{
new Man(){Age=21,Name="Adam",Adress="Shenzhen",Weight=60,Height=170},
new Man(){Age=21,Name="Adam",Adress="Shenzhen",Weight=60,Height=170}
};
var distinct = list.Distinct();
然而去重得到的distinct集合的Count依然为二,集合里依然存在两个Adam。
实际上,Distinct方法内进行比较的是声明的引用,而不是对象属性,就和对两个属性一模一样的对象使用Equals()方法得到的是False一样。
因此我们对对象集合使用Distinct方法时要使用重载Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
要使用这个方法,我们得重写IEqualityComparer接口,再使用Distinct方法:
public class ManComparer : <Man>
{
public bool Equals(Man x, Man y)
{
return x.Age == y.Age
&& x.Name == y.Name
&& x.Adress == y.Adress
&& x.Weight == y.Weight
&& x.Height == y.Height;
}
public int GetHashCode(Man obj)
{
return obj.GetHashCode();
}
}
var distinct = list.Distinct(new ManComparer());
然而,再一次,distinct集合内依然有两个对象。
实际上,由于直接获取对象的HashCode,用HashCode进行比较的速度比 Equals 方法更快,
因此IEqualityComparer内部会在使用 Equals 前先使用 GetHashCode 方法,在两个对象的HashCode都相同时即刻判断对象相等。
而当两个对象HashCode不相同时, Equals 方法就会被调用,对要比较的对象进行判断。
由于在上例中list中的两个引用实际上是两个不同的对象,因此HashCode必定不相同
所以要触发Equlas方法,我们需要改 GetHashCode ,让它返回相同的常量
public class ManComparerNew : IEqualityComparer<Man>
{
public bool Equals(Man x, Man y)
{
return x.Age == y.Age
&& x.Name == y.Name
&& x.Adress == y.Adress
&& x.Weight == y.Weight
&& x.Height == y.Height;
}
public int GetHashCode(Man obj)
{
return 1;
}
}
var distinct = list.Distinct(new ManComparerNew());
现在distinct集合中就只有一个Man对象了,成功实现了去重。
值类型
整型
- Sbyte:代表有符号的8位整数,数值范围从-128 ~ 127
- Byte:代表无符号的8位整数,数值范围从0~255
- Short:代表有符号的16位整数,范围从-32768 ~ 32767
- ushort:代表有符号的16位整数,范围从0 到 65,535
- Int:代表有符号的32位整数,范围从-2147483648 ~ 2147483648
- uint:代表无符号的32位整数,范围从0 ~ 4294967295
- Long:代表有符号的64位整数,范围从-9223372036854775808 ~ 9223372036854775808
- Ulong:代表无符号的64位整数,范围从0 ~ 18446744073709551615。
- char:代表无符号的16位整数,数值范围从0~65535。 Char类型的可能值对应于统一字符编码标准(Unicode)的字符集。
注意
有符号和无符号的区别在于有无数值方向,有符号的类型是有数值方向的,无符号则没有
浮点型
1 float
1.1 包含其表示范围内的全部整数有效位7位;
1.2 包含其表示范围内的部分小数(单精度)。
2 double
2.1 包含其表示范围内的全部整数有效位16位;
2.2 包含其表示范围内的部分小数(双精度);
2.3 包含所有float;
2.4 比float范围大,精度高。
3 decimal
3.1 包含其表示范围内的全部整数28 到 29 位有效位;
3.2 包含其表示范围内的部分小数(十进制精度);
3.3 包含的小数集合与上述两者不同;
3.4 范围最小,精度最高。
注意
符号位 指数位 尾数位可以了解一下
当然,decimal在大多数情况下是安全的,但浮点数在理论上是不安全的。
至于精度误差造成的显示问题,则是很容易修补的。浮点数会带来的问题以及整型能避免的问题就是一个:
譬如说从A帐户转账到B帐户,经计算得出结果是3.788888888888888元,那么我们从A帐户扣除这么多钱,B帐户增加这么多钱,但事实上A帐户不一定会扣除准确的数值,例如A帐户的金额在100000000000,那么这个时候100000000000 - 3.788888888888888运算结果很有可能是99999999996.211111111111112。而这个时候B帐户的金额为0则很有可能加上准确的数值,如3.788888888888888,这样一来,0.011111111111112元钱就会不见了,日积月累的,差额就会越来越大。
static void Main(string[] args)
{
//整型
int num = -1;
num = 1;
uint unum= 1;
//unum = -1;
char nums = '1';
//浮点型
float fnum = 0.666666f;
double dnum = 0.555555;
Console.WriteLine(fnum);
Console.WriteLine(dnum);
//保留N位四舍五入
Console.WriteLine("保留N位四舍五入 ");
Console.WriteLine(Math.Round(dnum, 2));
Console.WriteLine(dnum.ToString("f2"));
Console.WriteLine(dnum.ToString("0.00"));
Console.WriteLine("{0:0.00}", dnum);
//高精度十进制 如果没有后缀 m,则数字将被视为 double 类型并会生成编译器错误。
decimal denum = 30.555m;
Console.WriteLine(denum);
//浮点型之间的区别
double dd = 10000000000000000000000d;
dd += 1;
Console.WriteLine("{0:G50}", dd);
decimal dds = 10000000000000000000000000000m;
dds += 1.1m;
Console.WriteLine("{0:G50}", dds);
}
Enum枚举的含义:
Enum枚举:枚举是一组命名整型常量,枚举类型是使用 enum 关键字声明的。枚举是值类型,数据直接存储在栈中,而不是使用引用和真实数据的隔离方式来存储,其包含自己的值,且不能被继承或者传递继承,枚举中每个元素的基础类型是 int。可以使用冒号指定另一种整数值类型。
enum game
{
元神,
三国杀 = 1,
[Description("作业写完了?")]
[DataTest("你怎么睡得着")]
崩坏=2
}
public class DataTest : Attribute
{
public string Data { get; set; }
public DataTest(string data)
{
Data = data;
}
public DataTest() { }
}
static class EnumExtensions
{
public static string GetDescription(this Enum val)
{
var field = val.GetType().GetField(val.ToString());
var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
if (customAttribute == null) { return val.ToString(); }
else { return ((DescriptionAttribute)customAttribute).Description; }
}
public static string GetData(this Enum val)
{
var field = val.GetType().GetField(val.ToString());
var customAttribute = Attribute.GetCustomAttribute(field, typeof(DataTest));
return customAttribute == null ? val.ToString() : ((DataTest)customAttribute).Data;
}
}
[Flags]
enum enumname
{
元神 = 1,
三国杀 = 2,
崩坏 = 4
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine($"游戏是:{game.元神}");
Console.WriteLine($"游戏是:{(int)game.元神}");
Console.WriteLine($"游戏是:{game.三国杀}");
Console.WriteLine($"游戏是:{(int)game.三国杀}");
Console.WriteLine($"游戏是:{game.崩坏.GetDescription()}");
Console.WriteLine($"游戏是:{game.崩坏.GetData()}");
//flags
Console.WriteLine(enumname.三国杀);
Console.WriteLine((int)enumname.三国杀);
Console.WriteLine((enumname)3);
Console.WriteLine((game)3);
}
}
枚举搜索
Console.WriteLine(Enum.Parse(typeof(de),"0"));
注意
1、枚举使用enum关键字来声明,与类同级,枚举本身可以有修饰符,但枚举的成员始终是公开的,不能有访问修饰符,枚举本身的修饰符仅能使用Public和internal。
2、枚举是隐式密封的,不允许作为基类派生子类
3、枚举类型的枚举成员均为静态,且默认值为Int32类型
4、每个枚举成员均具有相关联的常数值,此值的类型就是枚举的底层数据类型,每个枚举成员的常数值必须在该枚举的底层数据类型的范围之内,如果没有明确指定底层数据类型则默认的数据类型是int类型。
5、枚举的符号就是等号前边的,枚举值就是等号后边的
结构体
他和类和相似,可以认为他是类的小姐妹,所有继承自System.ValueType的类型都是值类型,而其他类型都是引用类型
struct Books
{
public string name;
public double price;
public string remark;
//但是不能配置无参的构造函数
public Books(string Name, Double Price, string Remark)
{
name = Name;
price = Price;
remark = Remark;
}
public void Export()
{
Console.WriteLine($"书名:{name}\n价格:{price}\n注释:{remark}");
}
}
internal class Program
{
static void Main(string[] args)
{
//简单使用
//类与结构体区别之一结构体不用new实列化直接使用
//结构中的方法不能用 virtual 和 abstract 修饰符,但是可以用 override 修饰,用来重写父类的方法
Console.WriteLine("简单实用结构体");
Books books1;
//BooksClass booksclass;
//booksclass.name = "12";
books1.name = "Dynamic 365";
books1.price = 20;
books1.remark = "帮助开发人员了解dynamic 365系统";
Console.WriteLine($"书名:{books1.name}\n价格:{books1.price}\n注释:{books1.remark}");
Console.WriteLine("*************************************************************");
Books a1 = new Books("三国杀", 200, "很好看");
Books a2 = new Books("三国杀", 200, "很好看");
BooksClass b1 = new BooksClass("三国杀", 200, "很好看");
BooksClass b2 = new BooksClass("三国杀", 200, "很好看");
Console.WriteLine("结构体"+Equals(a1, a2));
Console.WriteLine("类" + Equals(b1, b2));
//相同点
Console.WriteLine("*************************************************************");
Console.WriteLine("结构体");
Books books2 = new Books("三国杀", 200, "很好看");
books2.Export();
Console.WriteLine();
Console.WriteLine("类");
BooksClass books = new BooksClass("三国杀", 200, "很好看");
books.Export();
//详细解释一下值类型和应用类型不同
Console.WriteLine("*************************************************************");
Console.WriteLine("值类型和应用类型不同");
BooksClass yingleixing1 = new BooksClass("",1,"");
BooksClass yingleixing2 = yingleixing1;
Books zhileixing1 = new Books("",1,"");
Books zhileixing2 = zhileixing1;
zhileixing2.price=2;
Console.WriteLine($"yingleixing1.price:{yingleixing1.price}\n" +
$"yingleixing2.price:{yingleixing2.price}\n" +
$"zhileixing1.price:{zhileixing1.price}\n" +
$"zhileixing2.price:{zhileixing2.price}\n");
}
注意
- 结构体不能配置无参
- 结构体可以不用new,类不行
可为空值类型
可为 null 值类型 T? 表示其基础值类型 T 的所有值及额外的 null 值。 例如,可以将以下三个值中的任意
一个指定给 bool? 变量: true 、 false 或 null 。 基础值类型 T 本身不能是可为空的值类型
static void Main(string[] args)
{
int? num= null;
num = 0;
//如何判断非空
if (num!=null)
{
Console.WriteLine("不为空");
}
if (num is int valueOfA)
{
Console.WriteLine($"a is {valueOfA}");
}
if (num.HasValue)
{
Console.WriteLine($"b is {num.Value}");
}
//从可为空的值类型转换为基础类型
int nums = num ?? 0;
}
元组
元组是使用轻量语法定义的类型。 其优点包括:更简单的语法,基于元素数量(称为“基数”)和元素类型的转换规则,以及一致的副本、相等测试和赋值规则。 但另一方面,元组不支持一些与继承相关的面向对象的语法。 C# 7.0 中的新增功能文章中的“元组”一节对其进行了概述。
static void Main(string[] args)
{
Tuple<int, int> tu = new Tuple<int, int>(1, 2);
Console.WriteLine(tu);
Console.WriteLine(tu.Item1);//访问元组第一个元素
//元组嵌套
Tuple<int, Tuple<int, int, int, int>> tu2 = new Tuple<int, Tuple<int, int, int, int>>(1, Tuple.Create(1, 2, 3, 4));
Console.WriteLine(tu2.Item1);//访问第一个元素
Console.WriteLine(tu2.Item2);//访问被嵌套的元组
Console.WriteLine(tu2.Item2.Item1);//访问被嵌套元组的第一个值
//简化版
Console.WriteLine("*****************************************************************");
(int, double) jianhua = (3, 1.2);
Console.WriteLine($"元组第一个值:{jianhua.Item1},元组第二个值:{jianhua.Item2}");
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
var t3 = (sum: 4.5, count: 3);
Console.WriteLine($"Sum of {t3.count} elements is {t3.sum}.");
//例子
Console.WriteLine("*****************************************************************");
var ints1 = new[] { 1, 2, 0,9 };
var listm1 = FindMinMax(ints1);
Console.WriteLine($"最大值:{listm1.max},最小值:{listm1.min}");
var ints2 = new[] { 1, 2, 0, 9 };
var (maxs,mins) = FindMinMax(ints2);
Console.WriteLine($"最大值:{maxs},最小值:{mins}");
(int max, int min) FindMinMax(int[] nums)
{
if (nums is null || nums.Length == 0)
{
throw new ArgumentException("值为空或者值未实例化");
}
var max = nums.Max();
var min = nums.Min();
return (max, min);
}
}
引用类型
Object类型
Object类型:其他所有类型的最终基类
Object类型是其他所有类型的最终基类,因为它是所有.NET Framework类的基类。它定义了一些基本的方法和属性,如ToString()、Equals()和GetHashCode(),这些方法和属性在所有.NET Framework类中都可用。因此,所有的.NET Framework类都继承自Object类型,这使得它成为其他所有类型的最终基类。
static void Main(string[] args)
{
//object
//比如我不想去声明一个class然后要将他放入一个list里面
Object human = new
{
name = "小明",
age = 18
};
List<object> strings= new List<object>();
strings.Add(human);
//为什么说object是所有基类的终极类
//object就像是树而其他东西就是他的树叶或者树枝,
Object ints, sttr, doubles, chars;
ints = 1;
sttr = "我是object哦";
doubles= 1.0;
chars = 'L';
Console.WriteLine($"Object:{ints},{sttr},{doubles},{chars}");
human human1=new human("小红","三好学生");
Object structs=human1;
human x = (human)structs;
Console.WriteLine("name:{0}\tdescription:{1}", x.name, x.description);
}
string类型
关键问题是“内存开销”,一个20000字符的string需要占用多少字节??
那么变成引用后你传递参数,需要多少字节??64位系统8字节就够了
值类型是传递副本,如果把20000字符副本传递过去开销很大的
ps:赋值一样不是核心,核心是内存分配和编译器优化
static void Main(string[] args)
{
//string 继承于object 他是一个值属于值类型的引用类型
string name = "小明";
string name2 = name;
name2 = "小红";
Console.WriteLine($"name:{name},name2:{name2}");
}
interface类型
接口其实简单使用他的时候你会发现他和类的继承很相似,那么用接口还是用继承?这取决我们的不同目标。如果我们要定义的一组方法和变量需要有不同的实现和取值,这时可以用接口,比如定义了“飞”这个方法,但是麻雀和老鹰的飞行肯定是不一样的,需要有不同的实现。有人说:这个用抽象类会更好。的确,因为抽象类还可以拥有一些别的共同数据,而麻雀和老鹰毕竟同属于鸟类,它们的相似点不只是“飞”这个方法,所以用继承更好。
但是如果飞机、风筝也要实现“飞”这个方法呢?麻雀、老鹰、飞机、风筝,假如它们之间除了“飞”这个方法没有任何共同点,并且它们的这个方法的实现也不尽相同,那么我们还要用继承吗?
也许这个时候你完全可以不理会它,直接无视这个共同点——这个共同点太小了,不值得我们去考虑。但是,也不完全是这样,有时候我们必须考虑,比如说造成飞机失事的最大原因之一:撞鸟。我们模拟这种事故的时候,抽象出来的模型是两个具有“飞”这个方法的对象在空中相撞。那么,这个共同点就被强化了需要引起重视了,于是接口派上用场了。
注意
其实在我初步去了解这个东西的时候,目前只能理解到初步的只能去降低代码的耦合度和规范。
interface shengfenid
{
void shenfen();
}
interface Human: shengfenid
{
void Nameis();
}
internal class Program:Human
{
static void Main(string[] args)
{
//将所有
Program human = new Program();
human.Nameis();
human.shenfen();
}
public void Nameis()
{
Console.WriteLine("我是小红");
}
public void shenfen()
{
Console.WriteLine("我的身份是预言家");
}
}
降低耦合度demo
interface Ihuman
{
void Namework();
}
class MiceName : Ihuman
{
public void Namework()
{
Console.WriteLine("我是Mice我的工作是作家");
}
}
class jockName : Ihuman
{
public void Namework()
{
Console.WriteLine("我是jock我的工作是作家");
}
}
internal class Program
{
static void Main(string[] args)
{
//接口是定义方法格式的一种
Ihuman human = new MiceName();
human.Namework();
Ihuman ihuman = new jockName();
human.Namework();
}
}
数组类型
一维数组
- 一维数组可以用来存储一组相同类型的数据,例如整数、浮点数、字符串等。
- 快速访问数据:由于一维数组中的元素是连续存储的,因此可以通过下标快速访问数组中的元素。
- 实现简单的算法:一维数组可以用来实现一些简单的算法,例如排序、查找等。
- 传递参数:一维数组可以作为函数的参数进行传递,方便实现一些复杂的操作。
- 存储图像、音频等数据:一维数组可以用来存储图像、音频等数据,方便进行处理和分析
//一维数组
int[] nums = new int[4] { 1, 8, 3, 4 };
Console.WriteLine("一维数组");
foreach (var item in nums)
{
Console.WriteLine(item);
}
二维数组
- 存储和处理表格数据:二维数组可以用来存储和处理表格数据,例如电子表格中的数据。
- 图像处理:二维数组可以用来存储和处理图像数据,例如像素值。
- 矩阵运算:二维数组可以用来进行矩阵运算,例如矩阵乘法、矩阵加法等。
- 游戏开发:二维数组可以用来表示游戏中的地图、角色位置等信息。
- 数据分析:二维数组可以用来存储和处理数据,例如统计分析、数据挖掘等。
- 算法实现:二维数组可以用来实现各种算法,例如图论算法、动态规划等。
- 网络编程:二维数组可以用来存储和处理网络数据,例如网络协议中的数据包。
- 数据库编程:二维数组可以用来存储和处理数据库中的数据,例如表格数据。
//二维数组
int[,] soue = new int[2, 3];//第一参数是行第二个参数是列
Console.WriteLine("输入:");//输入
for (int i = 0; i < soue.GetLength(0); i++)//这里是循环多少行也就是2,打印2次
{
for (int j = 0; j < soue.GetLength(1); j++)//这里是循环多少列也就是3,打印3次
{
soue[i, j] = Convert.ToInt32(Console.ReadLine());
}
}
Console.WriteLine("输出:");//输出
for (int i = 0; i < soue.GetLength(0); i++)
{
for (int j = 0; j < soue.GetLength(1); j++)
{
Console.Write(soue[i, j] + "\t");
}
Console.WriteLine("");
}
double[,] array = new double[,] { { 1, 1, 1 }, { 5, 5, 5 }, { 25, 25, 25 } };
int x = array.GetLength(0);
int y = array.GetLength(1);
Console.WriteLine("转置前:");
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
Console.Write(array[i, j] + "\t");
}
Console.WriteLine("");
}
double[,] zhuan = new double[x, y];
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
zhuan[j, i] = array[i, j];
}
}
Console.WriteLine("转置后:");
for (int i = 0; i < zhuan.GetLength(0); i++)
{
for (int j = 0; j < zhuan.GetLength(1); j++)
{
Console.Write(zhuan[i, j] + "\t");
}
Console.WriteLine("");
}
三维数组
- 在 .NET 中,三维数组可以用来存储和操作三维数据,例如立方体的体积、三维坐标系中的点、三维图像的像素等等。它可以用于各种数学、科学和工程应用,如计算机图形学、计算机辅助设计、物理模拟、医学成像等等。三维数组还可以用于存储和处理多维数据,例如视频流、音频信号等等。在 .NET 中,可以使用多种方式创建和操作三维数组,包括使用数组初始化器、循环和 LINQ 查询等等。
//三维数组
int[,,] arr = new int[3, 3, 3] { { { 10, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } },
{ { 10, 11, 12 }, { 13, 14, 15 }, { 16, 17, 18 } },
{ { 19, 20, 21 }, {22, 23, 24 }, { 25, 26, 27 } } };
Console.WriteLine(arr[0, 0, 0]);
交错数组
- .NET交错数组可以用来存储和处理多维数据,特别是在处理不规则数据时非常有用。交错数组是由一组数组组成的数组,每个数组的长度可以不同。这使得交错数组可以存储不同长度的行和列,从而更灵活地处理数据。交错数组可以用于图像处理、科学计算、游戏开发等领域。
int[][] socur = new int[3][];
socur[0]=new int[3];
socur[1]=new int[2];
socur[2]=new int[1];
socur[0][2] = 3;
socur[1][1] = 3;
foreach(int[] i in socur)
{
foreach (var item in i)
{
Console.Write(item+"\t");
}
Console.WriteLine("");
}
#region 杨辉三角
int[][] pascal = new int[10][];
for (int i = 0; i < pascal.Length; i++)
{
pascal[i] = new int[i + 1];
}
//赋值
pascal[0][0] = 1;
for (int i = 1; i < pascal.Length; i++)
{
pascal[i][0] = 1;//第一个元素
pascal[i][i] = 1;//最后一个元素
for (int j = 1; j < i; j++)
{
pascal[i][j] = pascal[i - 1][j - 1] + pascal[i - 1][j];//最主要
}
}
//输出
for (int i = 0; i < pascal.Length; i++)
{
for (int j = 0; j <= i; j++)
{
Console.Write("{0,4}", pascal[i][j]);
}
Console.WriteLine();
}
#endregion
委托
委托的含义:一个可以装很多方法的容器,像对象一样实例化她就可以调出所有方法。
delegate int Option(int a, int b);
static void Main(string[] args)
{
Option op = new Option(jiafa);
op += new Option(jianfa);
foreach (Option item in op.GetInvocationList())
{
Console.WriteLine($"委托多播的值:{item(3,4)}");
}
}
public static int jiafa(int a, int b)
{
return a + b;
}
public static int jianfa(int a, int b)
{
return a - b;
}
静态与非静态的区别
- 有无static ;
- 在静态类中既可以有静态成员,也可以有非静态成员。
- 非静态成员也称为【实例成员】。
- 在调用实例成员/方法(非静态成员)的时候,需要用实例对象.实例成员/方法(非静态成员)
- 在调用静态成员/方法的时候,需要用类名.静态成员/方法
- 静态方法(函数)中,只能访问静态成员,不能访问实例成员。
- 非静态方法(函数)中,既可以访问静态成员,也可以访问实例成员。
- 静态类中,只允许有静态成员/方法。
class Person
{
private string _s1 = "我是实例成员s1。"; //实例成员
private static string _s2 = "我是静态成员s2。"; //静态成员
public string S1 //实例成员
{
set { _s1 = value; }
get { return _s1; }
}
public static string S2 //静态成员
{
set { _s2 = value; }
get { return _s2; }
}
public void M1() //实例方法
{
Console.WriteLine(this.S1);
Console.WriteLine(Person.S2);
}
public static void M2() //静态方法
{
// Console.WriteLine(this.S1);
Console.WriteLine(Person.S2);
}
}
internal class Program
{
static void Main(string[] args)
{
//静态与非静态之间的差距
//实例p对象
Person p = new Person();
//调用实例方法
p.M1();
//调用静态方法
Person.M2();
}
}
注意
使用静态得准则就是:1.保证不会产生并发。2. 在方便快捷和开发难度上做一个衡量;
静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁,而实例化的则可以做销毁。
静态方法和静态变量创建后始终使用同一块内存,而使用实例的方式会创建多个内存。
程序结构
概念包括:程序、命名空间、类型、成员、程序集
一般C#文件扩展名为.exe或者.dll
Class和对象
类的访问级别
Public | 访问不受到限制 |
---|---|
Protected | 允许本类以及派生类进行访问 |
Internal | 访问仅限于当前程序集 默认的类访问级别 |
Protected Internal | 允许本类或派生类访问,注意比Internal的范围广 |
Private | 仅允许当前类访问,派生类不能访问 类里面的成员默认的访问级别 |
static class Demos
{
//如果不加上public 关键字就会报错
public static int num = 0;
public static string Hello()
{
return "我是静态方法"+num;
}
}
class Demos2
{
//如果不加上public 关键字就会报错
static int num = 0;
public static string Hello()
{
return "我是静态方法" + num;
}
}
internal class Program
{
static void Main(string[] args)
{
//Demos.num = 01;
Console.WriteLine(Demos.Hello());
//Demos2.num = 010;
}
}
C# 中,如果没有指定访问修饰符,类的默认访问级别是 internal(也称为程序集访问)。这意味着该类只能在同一个程序集内被访问(也就是说,只能在同一项目中的其他代码中使用)。需要注意的是,和类不同,类成员的默认访问级别是 private,即只有在该类中才可以被访问。因此,如果想要从类外部访问类成员,需要显式地将其声明为 public 或 protected 等可访问级别的修饰符。
类的特性
分为三个分别是:继承、多态、封装。
继承
继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。
当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。
继承的思想实现了 属于(IS-A) 关系。例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物
<访问修饰符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}
简单demo
class animal
{
public double Height { get; set; }
public double width { get; set; }
public double length { get; set; }
}
class Duck:animal
{
public double Volume()
{
return Height * width *length ;
}
}
internal class Program
{
static void Main(string[] args)
{
Duck duck = new Duck();
duck.Height= 10;
duck.length = 10;
duck.width= 10;
var VolumeValue=duck.Volume();
Console.WriteLine(VolumeValue);
}
}
多重继承
注意
这里多重继承并不是一个类去继承很多个类,而是通过接口去完成这样的继承。
//这里可能会有一个疑问点那就是结构体可以被继承吗
class animal
{
public double Height { get; set; }
public double width { get; set; }
public double length { get; set; }
}
public interface beishuzengzhang
{
int erbeishu(int volumeValue);
}
class Duck : animal, beishuzengzhang
{
public double Volume()
{
return Height * width * length;
}
public int erbeishu(int volumeValue)
{
return volumeValue * 2;
}
}
internal class Program
{
static void Main(string[] args)
{
Duck duck = new Duck();
duck.Height = 10;
duck.length = 10;
duck.width = 10;
var VolumeValue = duck.Volume();
Console.WriteLine(VolumeValue);
Console.WriteLine(duck.erbeishu((int)VolumeValue));
}
}
base的应用
用于派生类继承有参构造函数的基类
demo无参函数
#region 总结
//不加:base(),也会去执行Person的无参方法。
#endregion
public class Person
{
public Person()
{
Console.WriteLine("Person无参的构造函数");
}
}
public class Teacher : Person
{
public Teacher():base()
{
Console.WriteLine("Teacher无参的构造函数");
}
}
internal class Program
{
static void Main(string[] args)
{
Teacher teacher=new Teacher();
Console.WriteLine(teacher);
}
}
demo有参构造函数
#region 总结
//总结
//加了base参数后可以调用Person构造函数有参方法
//除去就只会执行Person构造函数无参方法
#endregion
public class Person
{
public Person()
{
Console.WriteLine("Person无参的构造函数");
}
public Person(string name,string sex,int age)
{
Console.WriteLine($"我的名字叫做{name},我的性别是{sex},我的年龄是{age}。");
}
}
public class Teacher : Person
{
public Teacher()
{
Console.WriteLine("Teacher无参的构造函数");
}
public Teacher(string name, string sex, int age):base(name, sex, age)
{
Console.WriteLine($"我的名字叫做{name},我的性别是{sex},我的年龄是{age}。");
}
//public Teacher(string name, string sex, int age)
//{
// Console.WriteLine($"我的名字叫做{name},我的性别是{sex},我的年龄是{age}。");
//}
}
internal class Program
{
static void Main(string[] args)
{
Teacher teacher = new Teacher("李三","男",18);
Console.WriteLine(teacher);
}
}
析构函数
C# 语言中的析构函数(Destructor)是一个特殊的方法,它与类同名,但名称前面加上 ~ 符号,用于释放该类对象占用的资源和内存。析构函数在对象被销毁之前调用,可以清理对象占用的非托管资源,如文件句柄、数据库连接等,从而避免资源泄漏。
C# 中的析构函数与 C++ 中的析构函数类似,都是以 ~ 开头的方法,但它们有一些区别。首先,C# 中的析构函数没有返回值,也不能被重载;其次,C# 中的析构函数不能手动调用,只有当垃圾回收机制销毁对象时才会自动被调用。
class Student
{
public int num { get; set; }
public Student()
{
num= 0;
Console.WriteLine("类中的构造函数");
}
~Student()
{
num = 2;
Console.WriteLine("类中的析构函数");
}
}
internal class Program
{
static void Main(string[] args)
{
Student s = new Student();
Console.WriteLine(s);
Console.WriteLine(s.num);
Student ss = s;
Console.WriteLine(ss.num);
}
}
IDisposable接口
class Student:IDisposable
{
public int num { get; set; }
public Student()
{
num = 0;
Console.WriteLine("类中的构造函数");
}
public void Dispose()
{
//在此函数进行清理
Console.WriteLine("开始清理");
}
}
internal class Program
{
static void Main(string[] args)
{
using (Student student = new Student())
{
}
}
}
多态
、数据库连接等,从而避免资源泄漏。
C# 中的析构函数与 C++ 中的析构函数类似,都是以 ~ 开头的方法,但它们有一些区别。首先,C# 中的析构函数没有返回值,也不能被重载;其次,C# 中的析构函数不能手动调用,只有当垃圾回收机制销毁对象时才会自动被调用。
class Student
{
public int num { get; set; }
public Student()
{
num= 0;
Console.WriteLine("类中的构造函数");
}
~Student()
{
num = 2;
Console.WriteLine("类中的析构函数");
}
}
internal class Program
{
static void Main(string[] args)
{
Student s = new Student();
Console.WriteLine(s);
Console.WriteLine(s.num);
Student ss = s;
Console.WriteLine(ss.num);
}
}
IDisposable接口
class Student:IDisposable
{
public int num { get; set; }
public Student()
{
num = 0;
Console.WriteLine("类中的构造函数");
}
public void Dispose()
{
//在此函数进行清理
Console.WriteLine("开始清理");
}
}
internal class Program
{
static void Main(string[] args)
{
using (Student student = new Student())
{
}
}
}