30天C#基础巩固-----值类型/引用类型,泛型,空合并操作符(??),匿名方法

简介: 30天C#基础巩固-----值类型/引用类型,泛型,空合并操作符(??),匿名方法

一:值类型/引用类型的区别


 

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


679140-20160602153343242-739030293.png


关于参数传递,这里有四种:


      值类型参数的按值传递;



      引用类型参数按值传递;


关于string引用类型参数按值传递的特殊情况;虽然string类型也是引用类型,然而在按值传递时,传递的实参却不会因方法中形参的改变而被修改。


class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("String引用类型按值传递的特殊情况");
        string str = "old string";         //引用类型
          ChangeStr(str);
        Console.WriteLine("调用之后的值:"+str);
        Console.ReadKey();
    }
    private static void ChangeStr(string oldStr)
    {
         oldStr = "New string";
        Console.WriteLine("方法中的oldStr:"+oldStr);
    }
}

679140-20160602153345227-2132679761.png


 造成这个原因是由于:string具有不变性,一旦一个string类型被赋值,则它是不会改变的,即不能通过代码来修改它的值,图中好像是对str进行了修改,但是由于string类型的不可变性,系统会重新分配一块内存空间来存放New  string字符串。把修改的内存首地址赋值给oldStr变量,所以值就发生了变化。


      值类型参数的按引用传递;


      引用类型参数的按引用传递;


这里就需要使用到ref,out这两个关键字了,他们是把值类型和引用类型都按照引用进行传递。


二:重新认识------泛型


    泛型(generic):就是通用类型,它可以代替任意的数据类型,是类型参数化,从而达到只实现一个方法就可以操作多种数据类型的目的。泛型将方法实现行为和方法操作的数据类型分离,实现代码的重用。


//用int作为实际参数来初始化泛型类型
  List<int> inList=new List<int>();
inList.Add(3);
//用string作为实际参数来初始化泛型类型
  List<string> inString=new List<string>();
inString.Add("ahui");

上面的就是泛型,List是.NET类库中实现的泛型类型,T是泛型参数(形参),想实例化一个泛型类型,就必须传入实际的类型参数。


/// <summary>
/// 定义一个泛型---比较各种类型的大小。 IComparable接口是因为里面有个CompareTo方法
/// </summary>
/// <typeparam name="T">泛型的参数,就是传递来的类型</typeparam>
public class Compare<T> where T:IComparable
{              
    public static T CompareGeneric(T t1,T t2)
    {
        if (t1.CompareTo(t2)>0)
        {
            return t1;
        }
        else
        {
            return t2;
        }
    }
}


这里面的T就是我们将来调用方法时往里面传递的类型(泛型的类型参数)。


CompareGeneric是实现泛型的方法,代码中的where语句是类型参数的约束,它用来使类型参数可以适用于CompareTo方法。从而对类型参数进行约束。

//调用泛型,直接点操作就可以来,只需要注意传递进去的类型就可以。
  Console.WriteLine(Compare<int>.CompareGeneric(1,2));
Console.WriteLine(Compare<string>.CompareGeneric("2222","111"));
Console.ReadKey();


  1:泛型除了可以实现代码的重用,还提供了更好的性能和类型安全特性。


  2:使用泛型可以减少装箱和拆箱带来的性能消耗。因为我们直接就是类型,不需要在将其转换为object类型再来进行操作。


  3:泛型代码中,T就是类型参数,无论调用类型方法还是初始化泛型实例,都需要用真实类型代替T。


  4:我们有的时候不需要给T赋值,编译器自己可以推测出T是什么类型的,这是类型参数的推断。(参数只能是一种类型)



  5:类型参数的约束:


共有4中约束,语法类似:约束要放在泛型方法或类型声明的末尾,并且要使用where关键字。


--->引用类型约束


     引用类型约束的表示形式为T:class,它确保传递的类型实参必须是引用类型。


/// <summary>
/// 引用类型约束
/// </summary>
/// <typeparam name="T"></typeparam>
public class Samplereference<T>where T:Stream      
{
    public void Test(T stream)
    {
        stream.Close();
    }
}


类型参数T设置了引用类型约束。where T:stream的意思就是告诉编译器,传递的类型必须是System.IO.Stream或者是其子类。


--->值类型约束


   表示形式:T:struct


/// <summary>
/// 值类型约束
/// </summary>
/// <typeparam name="T"></typeparam>
public class Samplevaluetype<T> where T:struct             //约束为结构体了,(值类型)
{
    public static T Test()
    {
        return new T();   //T是一个值类型,所有的值类型都有一个公共的无参构造函数,
    }
}


--->构造函数类型约束


   表示形式:T:new(),若是有多个约束此约束应该最后指定。


--->转换类型约束


   表示形式:T:基类名。T:接口名或T:U;


---->组合约束


就是将上面的都组合在一起,类必须放在接口前面,不同的类型参数可以有不同的约束,但每种类型参数必须分别使用一个单独的where关键字。



三:可空类型


   可空类型也是值类型,但它包含null值的值类型,


int? a=null;


int ?就是可空的int类型,?修饰符只是C#的语法糖,就是C#提供的一种方便的表示形式。


四:空合并操作符(??)



    ??操作符,它会对左右两个操作数进行判断,如果左边的数不为null,就返回左边的数。如果左边的数为Null,就返回右边的数。主要用于可空类型,也可以用于引用类型的判空,但是不能用于值类型。


/// <summary>
/// ??运算符
/// </summary>
private static void NullcoalescingOperator()
{
    int? nullable = null;
    int? nullhasvalue = 1;
    int x = nullable ?? 12;            //和三目运算符功能一样。
    int y = nullhasvalue ?? 123;
    Console.WriteLine("可空类型没有值:"+x);
    Console.WriteLine("可空类型有值:" + y);
    Console.WriteLine();
    //??运于引用类型
    string stringnotnull = "123";
    string stringisnull = null;
    string result = stringnotnull ?? "456";
    string result2 = stringisnull ?? "12";
    Console.WriteLine("引用类型不为Null的情况:"+result);
    Console.WriteLine("引用类型为Null的情况:" + result2);
}



static void Main(string[] args)


{
Console.WriteLine();
NullcoalescingOperator();
Console.ReadKey();
}


679140-20160602153346539-786078508.png



使用??运算符可以很方便地设置默认值,避免了通过if,else语句来进行判断,从而简化了代码行数,提高了代码的可读性。


五:匿名方法


解释:就是没有名字的方法,因为没有名字,匿名方法只能在函数定义(就是把方法的定义和实现嵌套在一起)的时候被用。其它任何 情况下都不能被调用。


委托是匿名函数的前提。


public class Friend
{
    /// <summary>
    /// 实现委托的方法(就是委托要传递的方法)
    /// </summary>
    /// <param name="nickName"></param>
    public void Vote(string nickName)
    {
        Console.WriteLine("昵称为:{0},来了呀",nickName);
    }
}



class Program
{
    //定义委托
    private delegate void VoteDelegate(string name);
    private static void Main(string[] args)
    {
        //实例化委托对象
        VoteDelegate voteDelegate = new VoteDelegate(new Friend().Vote); //Vote方法当作了参数来传递。
        voteDelegate("ahui"); //把ahui传递到了下面的方法中。
        Console.ReadKey();
    }
}

679140-20160602153348508-1487108594.png


下面是使用匿名函数

class Program
 {
     //定义委托
     private delegate void VoteDelegate(string name);
     private static void Main(string[] args)
     {
         //使用匿名函数来实例化委托。必须为delegate。其余就和方法的一样。
          VoteDelegate voteDelegate = delegate(string nickName)
         {
             Console.WriteLine("昵称为:" + nickName);
         };
         //通过调用委托来回调Vote()方法,这是隐式调用方式。
           voteDelegate("ahui");
         Console.ReadKey();
     }
 }


679140-20160602153349899-1255027132.png679140-20160602153351336-1503933861.png



我们使用匿名函数就不需要单独定义一个Vote方法,这减少了代码行数。


匿名函数的缺点:  


          不能被其他地方调用,容易形成闭包。







目录
相关文章
|
开发框架 .NET 程序员
C# 去掉字符串最后一个字符的 4 种方法
在实际业务中,我们经常会遇到在循环中拼接字符串的场景,循环结束之后拼接得到的字符串的最后一个字符往往需要去掉,看看 C# 提供了哪4种方法可以高效去掉字符串的最后一个字符
1254 0
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
294 65
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
584 2
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
651 3
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
476 1
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
290 11
|
C#
C#一分钟浅谈:Lambda 表达式和匿名方法
本文详细介绍了C#编程中的Lambda表达式与匿名方法,两者均可用于定义无名函数,使代码更简洁易维护。文章通过基础概念讲解和示例对比,展示了各自语法特点,如Lambda表达式的`(parameters) =&gt; expression`形式及匿名方法的`delegate(parameters)`结构。并通过实例演示了两者的应用差异,强调了在使用Lambda时应注意闭包问题及其解决策略,推荐优先使用Lambda表达式以增强代码可读性。
272 8
|
存储 Java C#
C# 中的值类型与引用类型
在 C# 编程中,值类型和引用类型的区别至关重要,直接影响内存管理、性能优化及编程模式选择。值类型直接存储数据(如 `int`、`float`),而引用类型存储数据的引用地址(如 `class`、`string`)。值类型的赋值涉及数据复制,适合小数据量;引用类型仅复制引用,适合大数据量处理但需关注垃圾回收。本文通过具体代码示例详细解析二者的定义、存储方式及性能影响,并提供实战案例分析及易错点避免方法,帮助读者更好地理解和应用。
325 2
|
C#
C#的方法的参数传递
C#的方法的参数传递
223 0
|
数据可视化 程序员 C#
C#中windows应用窗体程序的输入输出方法实例
C#中windows应用窗体程序的输入输出方法实例
320 0