C#--学习笔记

这篇博客详细介绍了C#中的关键概念,包括接口(Interface)、委托(Delegate)、事件(Event)以及类型系统。博主探讨了接口在代码组织中的作用,委托的使用步骤和在方法间传递的特性,以及事件的定义和订阅。此外,还涵盖了类型系统中的值类型和引用类型,以及泛型的概念和用途。文章进一步讨论了可空类型、匿名方法和迭代器,强调了匿名方法的闭包行为和迭代器在遍历集合中的作用。最后,博主提到了C# 4.0及后续版本的新特性,如动态类型(Dynamic)、多线程编程和异步编程模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、《C#学习笔记》
目录:C06 接口(interface)
      C08 委托(delegate)
      C09 事件(Event)
      C10 类型
      C11 泛型(Generic)--使类型参数化
      C12 可空类型、匿名方法和迭代器
      C14 Lambda表达式
      C15 扩展方法--使类的扩展更简单:(Extension Method)
      C16 LINQ--数据操作So easy
      C17 C#4.0中的微小改动
      C18 动态类型(dynamic)
      C19 多线程编程
      C20 异步编程
      C21 出师前闯关训练第一关--文件操作
      C22 出师前闯关训练第二关--网络编程
      C23 出师前闯关训练最后一关--使用GDI+实现属于你的截图工具

C06 接口(interface):
    接口可以理解为对一组方法声明进行的统一命名,但这些方法没有提供任何实现。
    通过接口,可以对方法进行统一管理,避免了在每种类型中重复定义这些方法。
     
C08 委托(delegate):
    1.委托(delegate)
    委托定义:"委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,
              可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。"
    使用委托的步骤为:1.定义委托类型->2.声明委托变量->3.实例化委托->4.作为参数传递给方法->5.调用委托。
    委托的最大作用:委托使得一个方法可以作为另一个方法的参数进行传递。
    引入委托之后,可以把函数作为参数传递给另一个方法,委托可以提高方法扩展性。
    
    2.委托链
    C#中把封装多个方法的委托称为委托链或多路广播委托。

C09 事件(Event):
    定义:事件基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。
                 应用程序需要在事件发生时响应事件。例如,中断。事件是用于进程间通信。
    事件定义的结构:访问修饰符 event 委托类型 事件名;例如:public event EventHandler birthday
    订阅和取消事件:

C10 类型:
    值类型和引用类型:
    值类型:主要包括简单类型、枚举类型、结构体类型等。值类型的实例通常被分配在线程的堆栈上,变量保存的内容就是实例数据本身。
    引用类型:主要包括类类型、接口类型、委托类型和字符串类型等。引用类型的实例则被分配再托管堆上,变量保存的是实例数据的内存地址。

C11 泛型(Generic)--使类型参数化:
    泛型代表的就是"通用类型",它可以代替任意的数据类型,使类型参数化,从而达到只实现一个方法就可以操作多种数据类型的目的。
    泛型将方法实现行为与方法操作的数据类型分离,实现了代码重用。
    (1) 类型参数:例如:在 List<T> 中,T就是类型参数。
        根据泛型类型参数是否提供实际类型,泛型分为两类:未绑定的泛型和已构造的泛型。
        开放类型和密封类型。
        开放类型是指包含类型参数的泛型,所有未绑定的泛型都属于开放类型;而封闭类型则是指那些已经为每一个类型参数都传递了实际数据类型的泛型。
    实现泛型类的时候,我们使用了where T:IComparable的代码,其中where语句用来使类型参数继承于IComparable接口,从而对类型参数进行约束。
    (2) 泛型中的静态字段和静态函数问题:
        每个封闭的泛型类型中都有属于它自己的静态字段。
    (3) 类型参数的推断:增强代码的可读性。
        // 泛型方法
        private static void genericMethod<T>(ref T t1, ref T t2)
        {
            T temp = t1;
            t1 = t2;
            t2 = temp;
        }
 
 C12 可空类型、匿名方法和迭代器:
     (1)Nullable<T>和Nullable :
        空合并操作符,即??操作符,它会对左右两个操作数进行判断:如果左边的数不为null,就返回左边的数;如果左边的数为null,就返回右边的数。
        注:??运算符不能应用于值类型。(除了可空类型外,其他的值类型都是不能与null类型进行比较的。)
        可空类型的装箱和拆箱:当把一个可空类型付给引用类型变量是,CLR会对可空类型(Nullable<T>)对象进行装箱处理;当把一个已装箱的值类型付给可空类型变量时,CLR会对已装箱的值类型进行拆箱处理。
     (2)匿名方法:没有名字的方法。    
        回顾委托的使用方法:
        代码:12.2.1_1
        class Program
        {
            // 定义投票委托
            delegate void VoteDelegate(string name);
            static void Main(string[] args)
            {
                // 使用Vote方法来实例化委托对象
                //VoteDelegate votedelegate = new VoteDelegate(new Friend().Vote);
                // 下面方式为隐式实例化委托对象方式,把方法直接赋给委托对象
                VoteDelegate votedelegate = new Friend().Vote;

                // 通过调用托来回调Vote()方法,此为隐式调用方式
                votedelegate("SomeBody");
                Console.Read();
            }

            public class Friend
            {
                // 朋友的投票方法
                public void Vote(string nickname)
                {
                    Console.WriteLine("昵称为:{0} 来帮Learning Hard投票了", nickname);
                }
            }
        }
        委托是用来包装方法的类类型,既然匿名方法也是方法,当然可以被委托类型包装了,所以我们还可以用匿名方法的方式去实现上面的代码:
        代码:12.2.1_2
        // 匿名方法的使用演示
        class Program
        {   
            // 定义投票委托
            delegate void VoteDelegate(string name);
            static void Main(string[] args)
            {
                // 使用匿名方法来实例化委托对象
                VoteDelegate votedelegate = delegate(string nickname)
                {
                    Console.WriteLine("昵称为:{0} 来帮Learning Hard投票了", nickname);
                };

                // 通过调用托来回调Vote()方法,此为隐式调用方式
                votedelegate("SomeBody");
                Console.Read();
            }
        }
        匿名方法的缺点:不能再其他地方被调用,即不具有复用性。
        闭包:指的是在匿名方法中捕捉(即引用)了外部变量。两个概念:外部变量和被捕捉的外部变量。
        代码:12.2.2
        class Program
        {
            // 定义闭包委托
            delegate void ClosureDelegate();

            static void Main(string[] args)
            {
                // 调用方法
                closureMethod();
                Console.Read();
            }

            // 闭包方法
            private static void closureMethod()
            {
                // outVariable和capturedVariable对于匿名方法而言都是外部变量
                string outVariable = "外部变量";

                //  而capturedVariable是被匿名方法捕获的外部变量
                string capturedVariable = "被捕获的外部变量";
                ClosureDelegate closuredelegate = delegate
                {
                    // localvariable是匿名方法中局部变量
                    string localvariable = "匿名方法局部变量";

                    // 引用capturedVariable变量
                    Console.WriteLine(capturedVariable + " " + localvariable);
                };

                // 调用委托
                closuredelegate();
            }
        }
        被匿名方法捕获的变量是真的变量,而非创建委托实例时该变量的值。被匿名方法捕获后,变量的生命周期会被延长。
        匿名方法是如何延长变量的生命周期的:(对于匿名方法捕捉到的变量,编译器会额外创建一个类来容纳它们)
        代码:代码12.2.2.2_2
         // 闭包演示
        class Program
        {
            // 定义闭包委托
            delegate void ClosureDelegate();

            static void Main(string[] args)
            {
                ClosureDelegate test = CreateDelegateInstance();
                // 此时会回调匿名方法输出count的值
                test();

                Console.Read();
            }

            // 闭包延长变量的生命周期
            private static ClosureDelegate CreateDelegateInstance()
            {
                // 外部变量
                int count = 1;
                ClosureDelegate closuredelegate = delegate
                {           
                    Console.WriteLine(count);
                    // 捕捉了外部变量
                    count++;
                };

                // 调用委托
                closuredelegate();

                return closuredelegate;
            }
        }
        运行结果:1
                 2
        (3)迭代器:
           迭代器记录了集合中的某个位置,它使程序只能向前移动。
           class Program
        {
            static void Main(string[] args)
            {
                Friends friendcollection = new Friends();
                foreach (Friend f in friendcollection)
                {
                    Console.WriteLine(f.Name);
                }

                Console.Read();
            }
      
            //  朋友类
            public class Friend
            {
                private string name;
                public string Name
                {
                    get { return name; }
                    set { name = value; }
                }
                public Friend(string name)
                {
                    this.name = name;
                }
            }

            // 朋友集合
            public class Friends : IEnumerable
            {
                private Friend[] friendarray;

                public Friends()
                {
                    friendarray = new Friend[]
                {
                    new Friend("张三"),
                    new Friend("李四"),
                    new Friend("王五")
                };
                }

                // 索引器
                public Friend this[int index]
                {
                    get { return friendarray[index]; }
                }

                public int Count
                {
                    get { return friendarray.Length; }
                }

                 C# 1.0
                 实现IEnumerable<T>接口方法
                //public IEnumerator GetEnumerator()
                //{
                //    return new FriendIterator(this);
                //}

                // C# 2.0中简化迭代器的实现
                public IEnumerator GetEnumerator()
                {
                    for (int index = 0; index < friendarray.Length; index++)
                    {
                        // 在C# 2.0中只需要使用下面语句就可以实现一个迭代器
                        yield return friendarray[index];
                    }
                }
            }

            //  C#1.0中实现迭代器代码——必须实现 IEnumerator接口
            public class FriendIterator : IEnumerator
            {
                private readonly Friends friends;
                private int index;
                private Friend current;
                internal FriendIterator(Friends friendcollection)
                {
                    this.friends = friendcollection;
                    index = 0;
                }

                // 实现IEnumerator接口中的方法
                public object Current
                {
                    get
                    {
                        return this.current;
                    }
                }

                public bool MoveNext()
                {
                    if (index + 1 > friends.Count)
                    {
                        return false;
                    }
                    else
                    {
                        this.current = friends[index];
                        index++;
                        return true;
                    }
                }

                public void Reset()
                {
                    index = 0;
                }
            }
        }

C14 Lambda表达式:                
    Lambda表达式可以理解为一个匿名方法,它可以包含表达式和语句,并且用于创建委托或转换为表达式树。
    在使用Lambda表达式时,都会使用"=>"运算符(读作"goes to"),该运算符的左边是匿名方法的输入参数,右边则是表达式或语句块。
    (1)Lambda表达式的演变过程:
        代码:14.1.1
        class Program
        {
            static void Main(string[] args)
            {
                // Lambda表达式的演变过程
                // 下面是C# 1中创建委托实例的代码
                Func<string, int> delegatetest1 = new Func<string, int>(Callbackmethod);

                //                                  ↓
                // C# 2中用匿名方法来创建委托实例,此时就不需要额外定义回调方法Callbackmethod
                Func<string, int> delegatetest2 = delegate(string text)
                {
                    return text.Length;
                };
                //                      ↓
                // C# 3中使用Lambda表达式来创建委托实例
                Func<string, int> delegatetest3 = (string text) => text.Length;

                //                                  ↓
                // 可以省略参数类型string,把上面代码再简化为:
                Func<string, int> delegatetest4 = (text) => text.Length;

                //                                  ↓
                // 此时可以把圆括号也省略,简化为:
                Func<string, int> delegatetest = text => text.Length;

                // 调用委托
                Console.WriteLine("使用Lambda表达式返回字符串的长度为: " + delegatetest("learning hard"));
                Console.Read();
            }

            // 回调方法
            private static int Callbackmethod(string text)
            {
                return text.Length;
            }
        }
     (2)Lambda表达式的使用:
        代码:14.1.2_1
        using System;
        using System.Windows.Forms;

        namespace _14._2
        {
            //演示用Lambda表达式去订阅事件
            //C#3.0之前的订阅代码
            class Program
            {
                static void Main(string[] args)
                {
                    // 新建一个button实例
                    Button button1 = new Button();
                    button1.Text = "点击我";

                    // C# 2中使用匿名方法来订阅事件
                    button1.Click += delegate(object sender, EventArgs e)
                    {
                        ReportEvent("Click事件", sender, e);
                    };
                    button1.KeyPress += delegate(object sender, KeyPressEventArgs e)
                    {
                        ReportEvent("KeyPress事件,即键盘按下事件", sender, e);
                    };

                    // C# 3之前初始化对象时使用下面代码
                    Form form = new Form();
                    form.Name = "在控制台中创建的窗体";
                    form.AutoSize = true;
                    form.Controls.Add(button1);
                    // 运行窗体
                    Application.Run(form);
                }

                // 记录事件的回调方法
                private static void ReportEvent(string title, object sender, EventArgs e)
                {
                    Console.WriteLine("发生的事件为:{0}", title);
                    Console.WriteLine("发生事件的对象为:{0}", sender);
                    Console.WriteLine("发生事件参数为: {0}", e.GetType());
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }
        代码:14.1.2_2(使用Lambda表达式)
        using System;
        using System.Windows.Forms;

        namespace _14._3
        {
            class Program
            {
                static void Main(string[] args)
                {
                    // 新建一个button实例
                    Button button1 = new Button() { Text = "点击我" };

                    // C# 3Lambda表达式方式来订阅事件
                    button1.Click += (sender, e) => ReportEvent("Click事件", sender, e);
                    button1.KeyPress += (sender, e) => ReportEvent("KeyPress事件,即键盘按下事件", sender, e);

                    // C# 3中使用对象初始化器
                    Form form = new Form { Name = "在控制台中创建的窗体", AutoSize = true, Controls = { button1 } };

                    // 运行窗体
                    Application.Run(form);
                }
                // 记录事件的回调方法
                private static void ReportEvent(string title, object sender, EventArgs e)
                {
                    Console.WriteLine("发生的事件为:{0}", title);
                    Console.WriteLine("发生事件的对象为:{0}", sender);
                    Console.WriteLine("发生事件参数为: {0}", e.GetType());
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }
    (3)表达式也有树结构--表达式树
        Lambda表达式除了可以用来创建委托外,还可以转换成表达式树。
        代码:14.2.1
        using System;
        // 引入Expression<TDelegate>类的命名空间
        using System.Linq.Expressions;

        namespace _14._6
        {
            class Program
            {
                // 构造a+b表达式树结构
                static void Main(string[] args)
                {
                    // 表达式的参数
                    ParameterExpression a = Expression.Parameter(typeof(int), "a");
                    ParameterExpression b = Expression.Parameter(typeof(int), "b");
            
                    // 表达式树主体部分
                    BinaryExpression be = Expression.Add(a, b);

                    // 构造表达式树
                    Expression<Func<int, int, int>> expressionTree = Expression.Lambda<Func<int, int, int>>(be, a, b);

                    // 分析树结构,获取表达式树的主体部分
                    BinaryExpression body = (BinaryExpression)expressionTree.Body;

                    // 左节点,每个节点本身就是一个表达式对象
                    ParameterExpression left = (ParameterExpression)body.Left;

                    // 右节点
                    ParameterExpression right = (ParameterExpression)body.Right;

                    // 输出表达式树结构
                    Console.WriteLine("表达式树结构为:"+expressionTree);
                    // 输出
                    Console.WriteLine("表达式树主体为:");
                    Console.WriteLine(expressionTree.Body);
                    Console.WriteLine("表达式树左节点为:{0}{4} 节点类型为:{1}{4}{4} 表达式树右节点为:{2}{4} 节点类型为:{3}{4}", left.Name, left.Type, right.Name, right.Type, Environment.NewLine);
            
                    Console.Read();
                }
            }
        }
        通过Lambda表达式来构造表达式树:
        代码:14.2.2
        using System;
        // 引入Expression<TDelegate>类的命名空间
        using System.Linq.Expressions;

        namespace _14._4
        {
            class Program
            {
                static void Main(string[] args)
                {
                    // 将lambda表达式构造成表达式树
                    Expression<Func<int, int, int>> expressionTree = (a, b) => a + b;

                    // 获得表达式树参数
                    Console.WriteLine("参数1:{0},参数2: {1}", expressionTree.Parameters[0], expressionTree.Parameters[1]);
                    // 获取表达式树的主体部分
                    BinaryExpression body = (BinaryExpression)expressionTree.Body;

                    // 左节点,每个节点本身就是一个表达式对象
                    ParameterExpression left = (ParameterExpression)body.Left;

                    // 右节点
                    ParameterExpression right = (ParameterExpression)body.Right;

                    Console.WriteLine("表达式树主体为:");
                    Console.WriteLine(expressionTree.Body);
                    Console.WriteLine("表达式树左节点为:{0}{4} 节点类型为:{1}{4}{4} 表达式树右节点为:{2}{4} 节点类型为:{3}{4}", left.Name, left.Type, right.Name, right.Type, Environment.NewLine);
                    Console.Read();
                }
            }
        }
    (3)如何把表达式树转换成可执行代码:
       通过Expression<TDelegate>类的Compile()方法将表达式树编译成委托实例,然后通过委托调用的方法得到两个数的和:
       using System;
        // 引入Expression<TDelegate>类的命名空间 //表达式树类
        using System.Linq.Expressions;

        namespace _14._5
        {
            class Program
            {
                static void Main(string[] args)
                {
                    // 将lambda表达式构造成表达式树
                    Expression<Func<int, int, int>> expressionTree = (a, b) => a + b;
                    // 通过调用Compile方法来生成Lambda表达式的委托
                    Func<int,int,int> delinstance =expressionTree.Compile();
                    // 调用委托实例获得结果
                    int result = delinstance(2, 3);
                    Console.WriteLine("2和3的和为:" + result);
                    Console.Read();
                }
            }
        }

C15 扩展方法--使类的扩展更简单:(Extension Method)
    定义:扩展方法,首先是一种方法,它可以用来扩展已定义类型中的方法成员。用扩展方法来为现有的类型添加方法,从而解决了使用继承进行扩展所带来的所有弊端。
    
C16 LINQ--数据操作So easy:    
    LINQ(Language Intergrated Query),即语言集成查询。LINQ的提出就是为了提供一种跨各种数据源的统一的查询方式。它主要包含4个组件--Linq to Objects、Linq to XML、Linq to DataSet和Linq to SQL。
    查询表达式:
        //查询表达式
        var queryExp=from s in collection
            select s;
        //点标记法  (另一种表达方式--点标记方式)
        var queryExp=collection.Select(s=>s);
    点标记法适合查询条件较少的情况,而查询表达式则更加注重结构化思维方式,类似于SQL语法。

C17 C#4.0中的微小改动:
    (1)可选参数和命名参数:最大的好处是简化了C#与COM(Component Object Model,即组件对象模型)组件的互操作。
    代码:17.1.2
        class Program
        {
            static void Main(string[] args)
            {
                // 省略name参数
                TestMethod(2, 14);
                // 省略了y和name参数
                TestMethod(2);

                // 为部分实参指定名称,使用命名实参只省略第二个参数
                TestMethod(2, name : "Hello");
                // 为所有实参指定名称
                TestMethod(x: 2, y: 20, name: "Hello");
                Console.Read();
            }

            // 带有可选参数的方法 //x为必备参数,y、name为可选参数
            static void TestMethod(int x, int y = 10, string name = "LearningHard")
            {
                Console.WriteLine("x={0} y={1} name={2}", x, y, name);
            }
        }
    使用C#来调用COM的操作。
    (2)泛型的可变性--协变性和逆变性:
       协变性:指的是泛型类型参数可以从一个派生类隐式地转化为基类。
       逆变性:指的是泛型类型参数可以从一个基类隐式地转化为派生类。
       只有接口和委托才支持协变和逆变(如:Func<out TResult>、Action<in T>),类或泛型方法的类型参数都不支持协变和逆变。
       协变和逆变只适用于引用类型,值类型不支持协变和逆变。(因为可变性存在引用转换的过程,而值类型变量存储的就是对象本身,并不是对象的引用)。

C18 动态类型(dynamic):
    动态类型的定义:适用dynamic关键字
        dynamic i=5; //动态类型定义

C19 多线程编程:
    (1).线程(Thread)是进程中的独立执行单元,对于操作系统而言,它通过调度线程来使应用程序工作。
        分为前台线程和后台线程。
        为了避免通过Thread手动创建线程而造成的性能损失(进程的创建和销毁会耗费大量时间),.NET引入了线程池机制。
    (2).线程池:是指用来存放应用程序中要使用的线程集合。
    (3).线程同步:
        线程同步技术是指在多线程程序中,为了保证后者线程,只有等待前者线程完成之后才能继续执行。
        使用监视器对象实现线程同步:
            监视器对象(Monitor)能够确保线程拥有对共享资源的互斥访问权,C#通过lock关键字来提供简化的语法。

C20 异步编程:
    (1).异步编程就是把耗时的操作放进一个单独的线程中进行处理(该线程需要将执行进度反映到界面上)。
    (2).异步编程模型--APM:
        APM(Asynchronous Programming Mode),即异步编程模型。
        异步编程模型允许程序用更少的线程取执行更多的操作。
        某个类是否实现了异步编程模型,主要看该类是否实现了返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法。
        缺点:不支持对异步操作的取消以及不能提供下载进度报告等。
    (3).异步编程模型--EAP:
        新的异步编程模型--基于事件的异步模式,即EAP(Event-based Asynchronous Pattern)。
        在.NET类库中,只有部分类实现了EAP,共17个。其中BackgroundWorker类,经常被用于实现网络文件的下载功能(暂停下载、下载进度报告、断点续传功能等)。
    (4).异步编程模型--TAP: C# 4.0
        更简单的异步编程实现方式--TAP(Task-based Asynchronous Pattern),即基于任务的异步模式。
        TaskAsync 为后缀的方法,通过向该方法传入CancellationToken参数(IsCancellationRequested属性来判断下载操作是否已被取消),通过IProgress<T>接口来实现进度报告的功能。
    (5).让异步编程So easy--C# 5.0中的async 和await

C21 出师前闯关训练第一关--文件操作:
    (1).文件操作核心类:File和FileInfo类
       .Directory和DirectoryInfo类
    (2).流(Stream):可以理解为内存中的字节序列。
    (3).对文件进行异步操作。

C22 出师前闯关训练第二关--网络编程:
    (1).网络编程基础知识:
        网络分层总览:计算机的网络通信可归结为网络中层与层之间的通信,OSI(Open System Interconnection)模型把网络通信分成7层:
                     物理层(physical layer)、数据链路层(data link layer)、网络层(network layer)、传输层(transport layer)、应用层(application layer)(最顶层)。    
        网络程序的工作机制:
            1)、IP地址:
               一般为32位(即4个字节)大小,采用点分十进制的格式来表示。例如:127.0.0.1。每个字节用一个十进制的整数来表示,并用点符号来分割各字节。
            2)、套接字(Socket): 
               (Socket--"插座")
                网络程序在进行通信时,主机A的程序将需要传输的信息写入其主机的Socket中,该Socket通过与网卡相连的传输介质将信息传输到主机B的Socket中。然后,主机B的程序从其Socket中读取该段信息,再将回复信息写入到Socket,并通过网络相连的传输介质将信息返回到主机A的Socket中。主机A中的程序从其Socket中读取回复信息,这样就完成了一次通信。
                两个程序之间的数据传输是通过套接字来完成的。我们可以将套接字理解为网络进程通信过程中所使用的一段缓冲区。
    (2).基于TCP的网络程序的实现:
        TCP协议是传输层协议,它提供了可靠的双向传输服务,是大多数网络应用的实现基础。
        TCP的工作过程:
            可划分为3个阶段:<1>连接的建立:3次握手(“发送一个检验包给对方然后互相确认的过程”),只有双方都接到确认信号后,连接才能建立起来。    
                            <2>传输数据。
                            <3>断开连接。
    (3).基于UDP的网络程序的实现:
        UDP和TCP都是构建在传输层(TP层之上)的协议。
        TCP是一种面向连接的、可靠的、面向字节流的传输协议;而UDP则是一种简单的、面向数据包的无连接协议,是一种不可靠的传输服务。这里的"无连接",指的是在正式通信前不必与对方先建立连接,即不管对方状态如何都直接将信息发送过去。
        UDP的工作原理:UDP将网络数据流量压缩成数据包的形式,每一个数据包使用8个字节(8*8位=64位)来描述包头信息,剩余字节则包含具体的传输数据。
                      UDP包头(只有8个字节)相当于TCP的包头(至少20个字节),它很短,由4个两字节肠的域组成,分别为源端口、目的端口、用户数据包长度和校验和。
        UDP的优势:1)UDP速度比TCP快
                  2)UDP有消息边界
                  3)UDP可以进行一对多传输

C23 出师前闯关训练最后一关--使用GDI+实现属于你的截图工具:
    (1).什么是GDI+:
        GDI是Graphics Device Interface Plus的缩写,即"图形设备接口",它提供了丰富的图形图像处理功能。
        在C# .NET中,我们通常使用GDI+来处理二维(2D)图形和图像,使用DirectX来处理三维(3D)图形图像。
    (2).笔、画笔和颜色:
        在GDI+中,可使用笔和画笔对象绘制图形和文本。
        以Pen类的实例来表示笔,用于绘制线段和空心形状;以Brush类实例来表示画笔,用于填充形状和绘制文本;以Color类的实例来表示颜色,用来为笔和画笔画出来的图形添加颜色。

完!

图书pdf及源码下载: https://download.youkuaiyun.com/download/jiemoxiangcha/10490158

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值