20200919_Notes_018&019

018传值输出引用数组具名可选参数扩展方法


一、传值参数:传值类型(副本)、传引用类型(地址)

  • 声明时不带修饰符的形参是值形参。一个值形参对应于一个局部变量
注意:
* 值参数创建变量的副本
* 对值参数的操作永远不影响变量的值
& 对象还是那个对象,但对象里的值(字段/属性)已经改变
namespace ParameterExample
{
    class Progream
    {
        static void Main(string[] args)
        {
            Student stu=new Student(){Name="Tom"};
            int y=100;
            stu.AddOne (y);
            System.Console.WriteLine(y);
            System.Console.WriteLine(stu.Name);
        }  
        //引用类型:创建新对象,实际上是两个地址;传参时,两个地址指向同一个堆上的实例,但是new之后,局部变量指向位置就变了
        static void SomeMethod(Student stu)
        {
            stu=new Student(){Name="Tim"};
            System.Console.WriteLine(stu.Name);
        }
        //引用类型:只更新对象,
        static void UpdateObject(Student stu)
        {
            stu.Name ="Tim";
            System.Console.WriteLine(stu.Name);
        }
    }
    class Student
    {
        public string Name{get;set;}
        //值类型:
        public void AddOne(int x)
        {
            x=x+1;
            System.Console.WriteLine(x);
        }
    }
}

二、输出参数:用于返回值外还需要输出得场景

  • 引用形参是用out修饰符声明的形参。
  • 输出形参并不创建新的存储位置。
  • 变量在可以作为输出形参传递之前不一定需要明确赋值
  • 在方法返回之前,该方法的每个输出形参都必须明确赋值
Console.WriteLine("Please input first number.");
string arg1 = Console.ReadLine();
double x = 0;
bool b1= double.TryParse(arg1, out x);
if (!b1)
{
    Console.WriteLine("input error!");
    return;
}
Console.WriteLine("Please input second number");
string arg2 = Console.ReadLine();
double y = 0;
bool b2 = double.TryParse(arg2, out y);
if (!b2)
{
    Console.WriteLine("input error.");
    return;
}
Console.WriteLine(x+y);
//实现Double 的TryParse方法
        public static bool TryParse(string input,out double result)
        {
            try
            {
                result = double.Parse(input);
                return true ;
            }
            catch (Exception ex)
            {
                result = 0;
                return false;
            }
        }

三、引用参数:用于需要修改实际参数值得场景

  • 引用形参是由ref修饰符声明的形参。
  • 引用形参并不创建新的存储位置。
  • 引用形参表示的存储位置恰是在方法调用中作为实参给出的那个变量所表示的存储位置。
  • 变量在可以作为引用形参传递之前,必须先明确赋值。
注意:
* 值类型:引用参数并不创建变量的副本;使用ref修饰符显式指出--此方法的副作用是改变实际参数的值
* 引用类型:引用参数并不创建变量的副本;使用ref修饰符显式指出--此方法的副作用是改变实际参数的值
* 

四、数组参数:用于简化方法的调用

            string str = "Tim,Tom,Amy,Liss";
            string[] result=str.Split(',', ',', ',');
            foreach (var item in result)
            {
                Console.WriteLine(item);
            }

五、具名参数:提供可读性

    PrintInfo(name: "Tim", age: 23);//

        
        static void PrintInfo(string name,int age)
        {
            Console.WriteLine("Hello {0},you are {1}",name,age);
        }

六、可选参数:参数拥有默认值

七、扩展方法(this参数):为目标数据类型“追加”方法

  • 方法必须是公有、静态的,即是被public static修饰
  • 必须是形参列表中得第一个,由this修饰
  • 必须由一个静态类(一般类名为SomeTypeExtension)来统一收纳对SomeType类型得扩展方法
  • LINQ方法
            double x = 3.14159;
            double y = x.Round(2);
            Console.WriteLine(y);
//扩展方法
    static class DoubleExtension
    {
        public static double Round(this double input,int digits)
        {
            double result = Math.Round(input, digits);
            return result;
        }
    }
            List<int> myList = new List<int>() { 9, 12, 13, 14, 15 };
            bool result = AllGreaterThanTen(myList);
            Console.WriteLine(result);

            bool ret = myList.All(i => i > 10);
            Console.WriteLine(ret);
        static bool AllGreaterThanTen(List<int> intList)
        {
            foreach (var item in intList )
            {
                if (item<10)
                {
                    return false;
                }
            }
            return true;
        }

我要和生活再死磕几年,我终将铸就辉煌,要么就走向毁灭,如果你发现我向平庸低了头,请向我开炮!
by 王甜甜(凯鲁亚克)

019委托详解


1、什么是委托

  • 委托(delegate)是函数指针的“升级版”
    • 实例:C/C++中的函数指针
#include <stdio.h>

typedef int (*Calc)(int a, int b);

int Add(int a, int b)
{
	int result = a + b;
	return result;
}
int Sub(int a, int b)
{
	int result = a - b;
	return result;
}
int main()
{
	int x = 100;
	int y = 200;
	int z = 0;
	z = Add(100, 200);
	printf("%d+%d=%d
", x, y, z);
	z = Sub(x, y);
	printf("%d-%d=%d
", x, y, z);

	Calc funcPoint1 = &Add;
	Calc funcPoint2 = &Sub;
	z = funcPoint1(x, y);
	printf("%d+%d=%d
", x, y, z);
	z = funcPoint2(x, y);
	printf("%d-%d=%d
", x, y, z);

	system("pause");
}
  • 一切皆指针
    • 变量(数据)是以某个地址为起点的一段内存中所存储的值
    • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
  • 直接调用与间接调用
    • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行-->返回
    • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行-->返回
  • Java中没有与委托相对应的功能实体
  • 委托的简单使用
//main函数中
            Calculator calculator = new Calculator();
            Action action = new Action(calculator.Report);
            Console.WriteLine("直接调用!");
            calculator.Report();
            Console.WriteLine("委托调用!");
            action.Invoke();
            action();

            Func<int,int ,int > func1 = new Func<int,int ,int>(calculator.Add );
            Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub );
            int x = 100;
            int y = 200;
            int z = 0;

            z=func1(x, y);
            Console.WriteLine(z);
            z=func2(x, y);
            Console.WriteLine(z);

    class Calculator
    {
        public void Report()
        {
            Console.WriteLine("I have 3 methos!");
        }
        public int Add(int a,int b)
        {
            return a + b;
        }
        public int Sub(int a,int b)
        {
            return a - b;
        }
    }

2、委托的声明

  • 委托是一种类(class),类是数据类型,所以委托也是一种数据类型
  • 它的声明方式与一般的类不同,主要是为了照顾可读性和C/C++传统
  • 注意声明委托的位置
    • 避免写错地方结果声明成嵌套类型
  • 委托与所封装的方法必须“类型兼容”
    • 返回值的数据类型一致
    • 参数列表在个数和数据类型上一致(参数名不需要一样)
using System;

namespace DelegateExample
{
    public delegate double Calc(double x, double y);
    class Program
    {
        //public delegate double Calc(double x, double y);      //嵌套类型

        static void Main(string[] args)
        {
            Calcultor calcultor = new Calcultor();
            Calc calc1 = new Calc(calcultor.Add);
            Calc calc2= new Calc(calcultor.Sub );
            Calc calc3 = new Calc(calcultor.Mul);
            Calc calc4 = new Calc(calcultor.Div );
            double a = 100;
            double b = 200;
            double c = 0;


        }
    }
    class Calcultor
    {
        public double Add(double x,double y)
        { return x + y; }
        public double Sub(double x, double y)
        { return x - y; }
        public double Mul(double x, double y)
        { return x * y; }
        public double Div(double x, double y)
        { return x / y; }
    }
}

3、委托的使用

  • 实例:把方法当作参数传给另一个方法
    • 正确使用1:模板方法,“借用”指定的外部方法来产生结果
      • 相当于“填空题”
      • 常位于代码中部
      • 委托有返回值
    • 正确使用2:回调(callback)方法,调用指定的外部方法
      • 相当于“流水线”
      • 常位于代码末尾
      • 委托无返回值
//模板方法
using System;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();

            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);

            Box box1 = wrapFactory.WrapProduct(func1);
            Box box2 = wrapFactory.WrapProduct(func2);

            Console.WriteLine(box1.Product.Name );
            Console.WriteLine(box2.Product.Name);

        }
    }
    class Product
    {
        public string Name { get; set; }
    }
    class Box
    {
        public Product Product { get; set; }
    }

    class WrapFactory
    {
        public Box WrapProduct(Func<Product> getProduct)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();
            box.Product = product;
            return box;
        }
    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            return product;
        }
    }
}
//回调函数
using System;

namespace DelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();
            Logger logger = new Logger();

            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
            Action<Product> log = new Action<Product>(logger.Log);

            Box box1 = wrapFactory.WrapProduct(func1,log);
            Box box2 = wrapFactory.WrapProduct(func2,log);

            Console.WriteLine(box1.Product.Name );
            Console.WriteLine(box2.Product.Name);

        }
    }
    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Product '{0}' created at {1}.Price is {2}",product .Name ,DateTime.UtcNow,product.Price);
        }
    }
    class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }
    class Box
    {
        public Product Product { get; set; }
    }

    class WrapFactory
    {
        public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallback)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();
            if (product.Price >=50)
            {
                logCallback(product);
            }

            box.Product = product;
            return box;
        }
    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            product.Price = 12;
            return product;
        }
        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            product.Price = 100;
            return product;
        }
    }
}
  • 注意:难精通+易使用+功能强大,一旦被滥用则后果非常严重
    • 缺点一:这是一种方法级别的紧耦合,现实工作中要慎之又慎
    • 缺点二:使可读性降低、debug的难度增加
    • 缺点三:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
//滥用委托
using System;

namespace BadDelegateUsingExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Operation opt1 = new Operation();
            Operation opt2 = new Operation();
            Operation opt3 = new Operation();

            opt3.INnerOperation = opt2;
            opt2.INnerOperation = opt1;
            opt3.Operate(new object(), null, null);
            //问题一:如果传入的两个参数为null,失败和成功的效果是什么?答:内层的操作会调用外层的回调
            //问题二:如果传入的两个参数不为null,会出现什么情况?答:所有默认的callback都被“穿透性”屏蔽
        }
    }
    class Operation
    {
        public Action DefaultSuccessCallback { get; set; }
        public Action DefaultFailureCallback { get; set; }
        public Operation INnerOperation { get; set; }

        public object Operate(object input,Action successCallback,Action failureCallback)
        {
            if (successCallback==null)
            {
                successCallback = this.DefaultSuccessCallback;
            }
            if (failureCallback==null)
            {
                failureCallback = DefaultFailureCallback;
            }
            object result = null;
            try
            {
                result = this.INnerOperation.Operate(input, successCallback, failureCallback);
                //Do something here
            }
            catch (Exception ex)
            {
                failureCallback.Invoke();
                //throw;
            }
            successCallback.Invoke();
            return result;
        }
    }
}
  • 缺点四:委托使用不当有可能造成内存泄漏和程序性能下降

4、委托的高级使用

  • 多播(multicast)委托
using System;
using System.Threading;

namespace MulticastDelegateExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red  };
            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);

            //单播委托
            action1.Invoke();
            action2.Invoke();
            action3.Invoke();

            //多播委托
            action1 += action2;
            action1 += action3;
            action1.Invoke();

        }
    }
    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor{ get; set; }
        public void DoHomework()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s)",this.ID ,i);
                Thread.Sleep(1000);
            }
        }
    }
}
  • 隐式异步调用
    • 同步与异步的简介
      • 中英文的语言差异
      • 同步:你做完了我(在你的基础上)接着做
      • 异步:咱们两个同时做(相当于汉语中的“同步进行”)
    • 同步调用与异步调用的对比
      • 每一个运行的程序是一个进程(process)
      • 每个进程可以有一个或者多个线程(process)
      • 同步调用实在同一个线程内
      • 异步调用是的底层机理是多线程
      • 串行同步单线程,并行异步多线程
//同步直接调用的主函数
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };

            stu1.DoHomework();
            stu2.DoHomework();
            stu3.DoHomework();

            for (int i = 0; i < 10; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}",i);
                Thread.Sleep(1000);
            }
//异步调用使用BegionInvoke
            action1.BeginInvoke(null, null);
            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);

            //!!!!!确保有10s让程序执行完成
            for (int i = 0; i < 10; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}", i);
                Thread.Sleep(1000);
            }
//异步调用使用Thread   
            Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
            Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
            Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));
            thread1.Start();
            thread2.Start();
            thread3.Start();
//使用task
            Task task1 = new Task(new Action(stu1.DoHomework));
            Task task2 = new Task(new Action(stu2.DoHomework));
            Task task3 = new Task(new Action(stu3.DoHomework));
            task1.Start();
            task2.Start();
            task3.Start();                     
  • 隐式多线程 v.s. 显式多线程
    • 直接同步调用:使用方法名
    • 间接同步调用:使用单播/多播委托的Invoke方法
    • 隐式异步调用:使用委托的BeginInvoke方法
    • 显式异步调用:使用Task或Thread
  • 应该适时地使用接口(interface)取代一些对委托的使用
    • Java完全地使用接口取代了委托的功能,即Java没有与C#中委托相对应的功能实体
using System;

namespace InterfaceReplaceDelegate
{
    class Program
    {
        static void Main(string[] args)
        {
            IProductFactory pizzaFactory = new PizzaFactory();
            IProductFactory toycarFactory = new ToyCarFactory();
            WrapFactory wrapFactory = new WrapFactory();

            Box box1 = wrapFactory.WrapProduct(pizzaFactory);
            Box box2 = wrapFactory.WrapProduct(toycarFactory);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);

        }
    }
    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Product '{0}' created at {1}.Price is {2}", product.Name, DateTime.UtcNow, product.Price);
        }
    }
    interface IProductFactory
    {
        Product Make();
    }
    class PizzaFactory : IProductFactory
    {
        public Product Make()
        {
            Product product = new Product();
            product.Name = "Pizza";
            product.Price = 12;
            return product;
        }
    }
    class ToyCarFactory : IProductFactory
    {
        public Product Make()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            product.Price = 100;
            return product;
        }
    }
    class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }
    class Box
    {
        public Product Product { get; set; }
    }

    class WrapFactory
    {
        public Box WrapProduct(IProductFactory productFactory)
        {
            Box box = new Box();
            Product product = productFactory.Make();
            box.Product = product;
            return box;
        }
    }

}

原文地址:https://www.cnblogs.com/mengwy/p/13737170.html