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();
    
  • 相关阅读:
    centos crash debug
    go get Unknown SSL protocol error in connection to gopkg.in
    Tensorflow serving with Kubernetes
    Spring 集成 Swagger UI
    Docker Registry V2 Garbage Collection
    Docker Registry V2 with Nginx
    Zabbix磁盘性能监控
    Zabbix CPU utilization监控参数
    Windows挂载Gluster复制卷
    Redis持久化存储(三)
  • 原文地址:https://www.cnblogs.com/yunquan/p/11260330.html
Copyright © 2011-2022 走看看