C#小王的笔记

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)。image-20230412150118540

5-2、堆的深入讲解

堆(Heap)是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除。image-20230412150227606

6、GC(Garbage Collection)垃圾收集器介绍

CLR的GC就是内存管理机制,我们写程序不需要关心内存的使用,因为这些都是CLR帮我们做了。image-20230412150214913

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对象了,成功实现了去重。

值类型

整型
  1. Sbyte:代表有符号的8位整数,数值范围从-128 ~ 127
  2. Byte:代表无符号的8位整数,数值范围从0~255
  3. Short:代表有符号的16位整数,范围从-32768 ~ 32767
  4. ushort:代表有符号的16位整数,范围从0 到 65,535
  5. Int:代表有符号的32位整数,范围从-2147483648 ~ 2147483648
  6. uint:代表无符号的32位整数,范围从0 ~ 4294967295
  7. Long:代表有符号的64位整数,范围从-9223372036854775808 ~ 9223372036854775808
  8. Ulong:代表无符号的64位整数,范围从0 ~ 18446744073709551615。
  9. 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");

            
        }

注意

  1. 结构体不能配置无参
  2. 结构体可以不用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();

        }
    }
数组类型
一维数组
  1. 一维数组可以用来存储一组相同类型的数据,例如整数、浮点数、字符串等。
  2. 快速访问数据:由于一维数组中的元素是连续存储的,因此可以通过下标快速访问数组中的元素。
  3. 实现简单的算法:一维数组可以用来实现一些简单的算法,例如排序、查找等。
  4. 传递参数:一维数组可以作为函数的参数进行传递,方便实现一些复杂的操作。
  5. 存储图像、音频等数据:一维数组可以用来存储图像、音频等数据,方便进行处理和分析
 //一维数组
            int[] nums = new int[4] { 1, 8, 3, 4 };
            Console.WriteLine("一维数组");
            foreach (var item in nums)
            {
                Console.WriteLine(item);
            }
二维数组
  1. 存储和处理表格数据:二维数组可以用来存储和处理表格数据,例如电子表格中的数据。
  2. 图像处理:二维数组可以用来存储和处理图像数据,例如像素值。
  3. 矩阵运算:二维数组可以用来进行矩阵运算,例如矩阵乘法、矩阵加法等。
  4. 游戏开发:二维数组可以用来表示游戏中的地图、角色位置等信息。
  5. 数据分析:二维数组可以用来存储和处理数据,例如统计分析、数据挖掘等。
  6. 算法实现:二维数组可以用来实现各种算法,例如图论算法、动态规划等。
  7. 网络编程:二维数组可以用来存储和处理网络数据,例如网络协议中的数据包。
  8. 数据库编程:二维数组可以用来存储和处理数据库中的数据,例如表格数据。
 //二维数组
            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;
        }

静态与非静态的区别

  1. 有无static ;
  2. 在静态类中既可以有静态成员,也可以有非静态成员。
  3. 非静态成员也称为【实例成员】。
  4. 在调用实例成员/方法(非静态成员)的时候,需要用实例对象.实例成员/方法(非静态成员)
  5. 在调用静态成员/方法的时候,需要用类名.静态成员/方法
  6. 静态方法(函数)中,只能访问静态成员,不能访问实例成员。
  7. 非静态方法(函数)中,既可以访问静态成员,也可以访问实例成员。
  8. 静态类中,只允许有静态成员/方法。
 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())
            {

            }
        }
    }
多态
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT中的好男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值