zoukankan      html  css  js  c++  java
  • C#个人笔记

    前言

    记录一下C#的一些东西,基础好多还是不会,还是推荐微软的官网文档,网上的博客写的都太水了,还是官网文档好一点

    微软官方文档

    异步任务

    同步方法的缺点

    其实我最想讲的就是这个,我举个例子,有两个方法,方法1和方法2,我现在想先执行方法1再执行方法2,如果我顺序执行的话,那么必须等待方法1执行完成之后才执行方法2 代码如下

    static void Main(string[] args)
    {
        method1();
        method2();
    }
    
    public static void method1()
    {
        for (int i = 0; i < 80; i++)
        {
    System.Console.WriteLine("method1: "+i);
        }
    }
    public static void method2() {
        for (int i = 0; i < 20; i++)
        {
    System.Console.WriteLine("method2: "+i);
        }
    }
    

    执行一下就知道了,必须等待方法1执行完才会执行方法2,就比如我想烧水做饭,必须先等水烧开了我才能洗菜切菜......这明明是可以同时做的事情,我们可以使用异步方法解决

    异步方法

    这个分为两种情况,I/O和CPU运算,我这里暂时没用到I/O所以不写了,讲讲CPU运算的

    返回Task

    static void Main(string[] args)
    {
        method1();
        method2();
        System.Console.ReadKey();
    }
    
    public static async Task method1()
    {
        await Task.Run(() =>
        {
     for (int i = 0; i < 80; i++)
     {
         System.Console.WriteLine("method1: " + i);
     }
        });
    }
    public static void method2() {
        for (int i = 0; i < 20; i++)
        {
     System.Console.WriteLine("method2: "+i);
        }
    }
    

    特点就是async,Task或者Task<T>,await,Task.Run这几个

    返回Task<T>

     static void Main(string[] args)
    {
        callMethod();
        System.Console.ReadKey();
    }
    
    public static async void callMethod()
    {
        Task<int> task = method1();
        int count = await task;
        method3(count);
    }
    public static async Task<int> method1()
    {
        int count=0;
        await Task.Run(() =>
        {
     for (int i = 0; i < 80; i++)
     {
         System.Console.WriteLine("method1: " + i);
         count++;
     }
        });
        return count;
    }
    public static void method2()
    {
        for (int i = 0; i < 20; i++)
        {
     System.Console.WriteLine("method2: " + i);
        }
    }
    public static void method3(int count)
    {
        System.Console.WriteLine("Count is "+count);
    }
    

    C#读取CSV,存入数据库

    C#读取CSV的内容,以DataTable的格式返回

                string path = @"D:360MoveDataUsersJustinDesktopdgkdataAudio Products~Accessories.csv";
    
    
    public static DataTable ReadData(string filePath)
            {
                //Encoding encoding = Common.GetType(filePath); //Encoding.ASCII;//
                Encoding encoding = Encoding.ASCII; //Encoding.ASCII;//
    
                DataTable dt = new DataTable();
                FileStream fs = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
    
                //StreamReader sr = new StreamReader(fs, Encoding.UTF8);
                StreamReader sr = new StreamReader(fs, encoding);
                //string fileContent = sr.ReadToEnd();
                //encoding = sr.CurrentEncoding;
                //记录每次读取的一行记录
                string strLine = "";
                //记录每行记录中的各字段内容
                string[] aryLine = null;
                string[] tableHead = null;
                //标示列数
                int columnCount = 0;
                //标示是否是读取的第一行
                bool IsFirst = true;
                //逐行读取CSV中的数据
                while ((strLine = sr.ReadLine()) != null)
                {
                    if (IsFirst == true)
                    {
                        tableHead = strLine.Split(',');
                        IsFirst = false;
                        columnCount = tableHead.Length;
                        //创建列
                        for (int i = 0; i < columnCount; i++)
                        {
                            DataColumn dc = new DataColumn(tableHead[i]);
                            dt.Columns.Add(dc);
                        }
                    }
                    else
                    {
                        //MySplit这个方法看下面的介绍
                            List<string> dataList = MySplit(strLine);
                            aryLine = dataList.ToArray();
                        DataRow dr = dt.NewRow();
                        for (int j = 0; j < columnCount; j++)
                        {
                            dr[j] = aryLine[j];
                        }
                        dt.Rows.Add(dr);
                    }
                }
                if (aryLine != null && aryLine.Length > 0)
                {
                    dt.DefaultView.Sort = tableHead[0] + " " + "asc";
                }
    
                sr.Close();
                fs.Close();
                return dt;
            }
    

    然后接受这个DataTable

    //先获取所有的列名
                DataTable dt = Read.ReadData(path);
                string[] strColumns = null;
                if (dt.Columns.Count > 0)
                {
                    int columnNum = 0;
                    columnNum = dt.Columns.Count;
                    strColumns = new string[columnNum];
                    for (int i = 0; i < dt.Columns.Count; i++)
                    {
                        strColumns[i] = dt.Columns[i].ColumnName;
                    }
                }
    
    
    //在遍历开始处理数据
                foreach (DataRow dataRow in dt.Rows)
                {
                    foreach (var columsName in strColumns)
                    {
                        switch (columsName)
                        {
                            case "Datasheets":
                                break;
                            case "Image":
                                break;
    						处理逻辑......
                        }
    
                        string aaa = dataRow[columsName].ToString();
                        处理逻辑......
                        Console.WriteLine(aaa);
                    }
                }
    

    Split(',')过滤掉双引号内的逗号

    这个也可以叫做,C#读取CSV文件逗号问题

    我读取的一串字符串是这样的

    "许嵩","蜀,云泉",1,22,"音乐"

    我使用Split(',')之后蜀云泉就分开了,这显然不是我要的结果

    解决方法可以使用正则,但是我不会写,所以写一个最基础的substring

            private static List<string> MySplit(string str)
            {
                const char mark = '"';
                const char comma = ',';
    
                bool startMark = false;
                int startIndex = -1;
                int endIndex = -1;
    
                List<string> myList = new List<string>();
                for (int i = 0; i < str.Length; i++)
                {
                    if (str[0] == comma)
                    {
                        myList.Add("");
                    }
                    if (startMark && str[i] == comma)
                    {
                        continue;
                    }
                    if (str[i] == comma && i > 0)
                    {
                        endIndex = i;
                    }
                    if (str[i] == mark && !startMark)
                    {
                        startMark = true;
                    }
                    else if (str[i] == mark && startMark)
                    {
                        startMark = false;
                    }
                    if (startIndex == -1)
                    { startIndex = i; }
                    if ((startIndex >= 0 && endIndex > 0) || (endIndex == -1 && i == str.Length - 1))
                    {
                        if (endIndex == -1)
                        {
                            endIndex = i + 1;
                        }
                        myList.Add(str.Substring(startIndex, endIndex - startIndex));
                        startIndex = -1;
                        endIndex = -1;
                    }
                }
                return myList;
            }
    

    这个strLine就是C#读取CSV的一行内容

    用好代码代替注释

    如开发人员发现需要写注释才能说清楚代码块的功用,应考虑重构,而不是洋洋洒洒写一堆注释。写注释来重复代码本来就讲得清的事情,只会变得臃肿,降低可读性,还容易过时,因为将来可能更改代码但没有来得及更新注释。

    设计规范

    1. 不要使用注释,除非代码本身“一言难尽”。

    2. 要尽量写清楚的代码而不是通过注释澄清复杂的算法。

    C#的13种基元类型

    所谓的基元类型,就是C#中的所有类型的基础,分别有8种整数类型,2种小数类型,1种金融类型,1种布尔类型,1种字符类型:

    金融类型是Decimal,布尔Bool,字符类型char

    避免使用隐式类型

    所谓的隐式类型就是var

    var name = "许嵩";
    string name = "许嵩";
    

    我使用var或者string都是一样的,在最终的CIL代码里面也没区别,但是,如果确定类型,还是直接指定类型好,一目了然

    引用参数ref和输出参数out

    先说结论

    1. ref参数:将变量带入一个方法中改变之后在带出方法,ref参数使用前必须赋值

    2. out参数: 在返回多个值的时候使用out参数,使用前不需要赋值

    举个例子,代码如下

            static void Main(string[] args)
            {
                int salary = 5000;
    
                jiangJin(salary);
    
                Console.WriteLine(salary);
                Console.Read();
    
            }
    
            static void jiangJin(int salary)
    
            {
                salary += 500;
            }
    

    像这个例子,输出的salary还是5000,虽然我经过了jiangJin方法的计算,但是我没有return计算后的结果,所以不管方法内怎么计算了,只要不return,salary值没变

    现在我加一个ref就不同了

            static void Main(string[] args)
            {
                int salary = 5000;
    
                jiangJin(ref salary);
    
                Console.WriteLine(salary);
                Console.Read();
    
            }
    
            static void jiangJin(ref int salary)
            {
                salary += 500;
            }
    

    我就加了一个ref,然后salary的输出结果就是5500了,不需要return了

    所以ref参数的作用是:将变量带入一个方法中改变之后在带出方法,以传引用的方式来传变量,而不是值拷贝的方式

    out输出参数其实和ref功能一模一样,但是out输出参数更注重检查方法内是否对out参数进行赋值,代码如下

            static void Main(string[] args)
            {
                int a = 1;
                int b;
                int asd = Calcu(a,out b);
                Console.WriteLine(asd + " : " + b);
    
                Console.Read();
            }
            static int Calcu(int a, out int b)
    
            {
                b = a;
                return a + b;
            }
    

    泛型

    复制代码的麻烦

    我现在写一个类,如下

        public class StudyT
    
        {
    
            public void Add(string name)
    
            {
                Console.WriteLine("我是增加方法,变量是:" + name);
            }
    
        }
    

    然后我可以实例化调用

     StudyT<string> studyT = new StudyT<string>();
    
     studyT.Add("许嵩");
    

    但是我的Add方法,我希望string类型可以,int类型可以,float类型的也可以使用,那我怎么办呢?

    复制一下StudyT类,然后Add方法的参数类型改为int,这当然ok,但是麻烦

    Object的装箱拆箱损失性能

    所以我选择使用Object类型,如下

        public class StudyT
    
        {
    
            public void Add(object name)
    
            {
                Console.WriteLine("我是增加方法,变量是:" + name);
            }
    
        }
    

    非常好,Object是基类,这下我传入int,string,float都可以用,但是又来了一个新问题,Object转化的时候有装箱拆箱,损失性能了,而且还有赋值不检查类型的错误可能,所以,我选择使用泛型

    泛型的好处

        public class StudyT<T> 
    
        {
    
            public void Add(T name)
            {
                Console.WriteLine("我是增加方法,变量是:" + name);
            }
    
        }
    

    泛型的使用方法就是

    1. 类后加

    2. 方法类型使用T表示

    这下我实例化对象调用的时候,传入什么类型,就是什么类型,而且还有类型检查,很安全

    泛型的约束

    我这个方法啊,只希望某个类或者某个接口才能使用,你给我传入一个int,string类型的没用,所以我做个约束,你传入的类型,必须是我想要的指定类型

        public class StudyT<T> where T : IMovie
    
        {
    
            public void Add(T name)
            {
                Console.WriteLine("我是增加方法,变量是:" + name);
            }
    
        }
    

    也很简单,直接 where T : Movie 即可,表明,传入的类型必须是继承了IMovie接口的,不管是大电影,微电影,动画片,科幻片啥的,只要继承了IMovie接口就能使用

    委托

    我终于知道委托和事件是干嘛的了,多亏我同学写的demo,不然我还是不理解委托

    书上说委托可以解决大量if else的情况,百科也是这样说的,但是我没啥感觉,出了一个排序的例子,我没感觉其他例子可以解决大量if else的,暂时不管这个了

    对于委托最好的理解和使用就是发布订阅模式了,在设计模式里面也称之为观察者模式

    发布订阅模式

    这个例子很清楚的讲解了委托的使用,我有3个类,服务器,客户端,消息管理类,代码如下

        class Server
        {
            public void PublishInfo(string info)
            {
                Console.WriteLine($"服务器发布了新消息: {info}");
                InformationManager.instance.Info = info;
                InformationManager.instance.UpdateInformation?.Invoke();
    
            }
        }
    
        class Client
        {
            string clientName = "";
            public Client(string name,bool isSub = false)
            {
                clientName = name;
                if (isSub)
                {
                    InformationManager.instance.UpdateInformation += ReceiveInfo;
                }
            }
    
            public void ReceiveInfo()
            {
                Console.WriteLine($"{clientName}用户收到了消息: {InformationManager.instance.Info}");
            }
        }
    
        class InformationManager
        {
            private string mInfo;
            public Action UpdateInformation;
    
            //单例的消息管理器实例
            private static InformationManager _instance;
            public static InformationManager instance
            {
                get
                {
                    if (_instance == null)
                    {
                        _instance = new InformationManager();
                    }
                    return _instance;
                }
            }
        }
    

    然后Main方法调用如下

     Server server = new Server();
     Client client = new Client("刘备",true);
    
     Client client1 = new Client("关羽");
    
     Client client2 = new Client("张飞");
    
     server.PublishInfo("好消息,许嵩发新歌啦");
    

    结果很Nice,这就是委托了

    不安全的委托

    我们在给委托添加方法的时候,使用的是+=

    InformationManager.instance.UpdateInformation += ReceiveInfo;
    

    但是有时候我们会不小心写成=,这样订阅者就会被覆盖,我有3个订阅者,结果写成了=,只有第3个订阅者收到消息了,前两个被覆盖了,这样很不好.

    不要说你会小心的,你不会忘记写+=,这是无法避免的事情,因为我刚学的时候也总是忘记写成=号,这就是不安全的委托,所以我们需要修改一下,使用事件解决这个问题

    事件,就是安全的委托

    事件:安全的委托

    上面说了,委托方法的+=很容易被写成=,这样不安全,所以我们改一下代码,使用事件,事件是安全的委托,因为事件强制你写+=

        class Server
        {
            public void PublishInfo(string info)
            {
                Console.WriteLine($"服务器发布了新消息: {info}");
                InformationManager.instance.Info = info;
                //InformationManager.instance.UpdateInformation?.Invoke(); 如果是委托需要调用
            }
        }
        class Client
    
        {
            string clientName = "";
            public Client(string name,bool isSub = false)
            {
                clientName = name;
                if (isSub)
                {
                    //这里的委托必须是+=,写成=就覆盖了,虽然我知道,但是我又忘了,所以写成事件,事件强制+=,所以事件是安全的委托
                    InformationManager.instance.UpdateInformation += ReceiveInfo;
                }
            }
    
            public void ReceiveInfo()
            {
                Console.WriteLine($"{clientName}用户收到了消息: {InformationManager.instance.Info}");
            }
        }
        class InformationManager
    
        {
            private string mInfo;
            //public Action UpdateInformation; 委托
            public event Action UpdateInformation;
    
            //单例的消息管理器实例
            private static InformationManager _instance;
            public static InformationManager instance
            {
                get
                {
                    if (_instance == null)
                    {
                        _instance = new InformationManager();
                    }
                    return _instance;
                }
            }
    
            /// <summary>
            /// 这个方法是事件的时候才用的,委托是直接调用,事件是触发,所以触发
            /// </summary>
            public string Info
            {
                get => mInfo;
                set
                {
                    if (value != mInfo)
                    {
                        mInfo = value;
                        if (UpdateInformation != null)
                        {
                            UpdateInformation();
                        }
                    }
                }
            }
    
        }
    

    委托需要调用,而事件是用来触发的,所以在InformationManager加了一个触发事件

    这次再给委托添加方法的时候试试,必须写成+=,这样就再也不怕写成=号了

    反射

                //反射第一种:GetType()  有实例对象,可以调用获取属性,方法,字段
                DateTime dateTime = new DateTime();
                Type type = dateTime.GetType();
                PropertyInfo[] propertyInfos = type.GetProperties(); //所有的属性
                MethodInfo[] methodInfos = type.GetMethods(); //所有的方法
                FieldInfo[] fieldInfos = type.GetFields(); //所有的字段
    
                //反射第二种:typeof()   没有实例对象的情况,比如静态类或单纯的类名
                Type type1 = typeof(X);
                PropertyInfo[] xpropertyInfos = type1.GetProperties(); //所有的属性
                MethodInfo[] xmethodInfos = type1.GetMethods(); //所有的方法
                FieldInfo[] xfieldInfos = type1.GetFields(); //所有的字段
    
                Type type2 = typeof(StudyThread);
                MethodInfo[] tmethodInfos = type2.GetMethods(); //所有的方法
                StudyThread studyThread = (StudyThread)Activator.CreateInstance(type2);//Activator是根据Type获取实例对象
                studyThread.Test();
    
  • 相关阅读:
    继承
    面向对象
    数据库的数据操作
    数据库数据类型以及建库语句
    第一天
    继承与多态
    C#面向对象——对象成员、方法重载、引用类库等
    C#面向对象初步
    SQL2008知识回顾
    C#知识回顾
  • 原文地址:https://www.cnblogs.com/yunquan/p/11260330.html
Copyright © 2011-2022 走看看