C#4.0 协变与逆变(类型转换)

前言:确实没有遇到过类似的问题,作为补充知识了解学习下。

概念:可变性,在C#编程中,由于存在类型之间的强制转换,很容易会出现所谓的类型可变性的说法,存在协变、逆变、不变三种。

概念:从本质上看,协变与逆变属于类型可变性的范畴,是.Net为了支持更广泛的隐式类型转换而存在的然而接口泛型和委托泛型不能进行这种隐式类型转换。

一.揭示 in 与out 关键字

1.in关键字

1.1 在foreach中使用,foreach语句只适合于System.Collections.IEnumberable或是System.Collections.Generic.IEnumberable<T>接口的集合。

1.2 Join in 或是Linq in

1.3 泛型接口、或是委托中的参数。实现逆变

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class xiebianandnibian : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //接口的逆变
        //INotifier<INotification> notifier = new Notifier<INotification>();//用类对象实现接口对象
        INotifier<MailNotification> mailNotifier = new Notifier<INotification>();  //逆变
        mailNotifier.Notify(new MailNotification());

        //委托的逆变
        Calculator c = new Calculator(Double);
        c(new chengfa());
    }

    #region 委托逆变
    delegate void Calculator(in jiafa x);
    static void Double(in jiafa x) { x.cal(); }
    #endregion
}

#region 接口逆变
//接口
public interface INotification
{
    string Message { get; }
}


//泛型接口
public interface INotifier<in TNotification> where TNotification : INotification 
{
    //方法
    void Notify(TNotification notification);
}


//继承泛型接口,生成类1
public class Notifier<TNotification> : INotifier<TNotification> where TNotification : INotification
{
    public void Notify(TNotification notification) 
    {
        Debug.Log(notification.Message);
    }
}

//抽象类
public abstract class Notification : INotification
{
    public abstract string Message { get; }
}


//继承泛型接口,生成类2
public class MailNotification : Notification 
{
    public override string Message
    {
        get { return "You got a email"; }
    }
}
#endregion

#region 委托逆变
public abstract  class jiafa
{
    private int x = 1;
    public void cal() 
    {
        Debug.Log("cal"+ x);
    }
}

public class chengfa : jiafa 
{

}

#endregion

1.4 C#——in 参数修饰符_吴俊荣的博客-CSDN博客_in 参数修饰符

通过理解使用 in 参数的动机,可以理解使用按值方法和使用 in 参数方法的重载决策规则。 定义使用 in 参数的方法是一项潜在的性能优化。 某些 struct 类型参数可能很大,在紧凑的循环或关键代码路径中调用方法时,复制这些结构的成本就很高。 方法声明 in 参数以指定参数可能按引用安全传递,因为所调用的方法不修改该参数的状态。 按引用传递这些参数可以避免(可能产生的)高昂的复制成本。

为调用站点上的参数指定 in 通常为可选。 按值传递参数和使用 in 修饰符按引用传递参数这两种方法并没有语义差异。 可以在调用站点选择 in 修饰符,因为你不需要指出参数值可能会改变。 在调用站点显式添加 in 修饰符以确保参数是按引用传递,而不是按值传递。 显式使用 in 有以下两个效果:

首先,在调用站点指定 in 会强制编译器选择使用匹配的 in 参数定义的方法。 否则,如果两种方法唯一的区别在于是否存在 in,则按值重载的匹配度会更高。

其次,指定 in 会声明你想按引用传递参数。 结合 in 使用的参数必须代表一个可以直接引用的位置。 out 和 ref 参数的相同常规规则适用:不得使用常量、普通属性或其他生成值的表达式。 否则,在调用站点省略 in 就会通知编译器你将允许它创建临时变量,并按只读引用传递至方法。 编译器创建临时变量以克服一些 in 参数的限制:
1.临时变量允许将编译时常数作为 in 参数。
2.临时变量允许使用属性或 in 参数的其他表达式。
3.存在从实参类型到形参类型的隐式转换时,临时变量允许使用实参。

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`

现在,假设可以使用另一种使用按值参数的方法。 结果的变化如以下代码所示:

static void Method(int argument)
{
    // implementation removed
}

static void Method(in int argument)
{
    // implementation removed
}

Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`

1.5 参数的限制

异步方法,通过使用 async 修饰符定义。
迭代器方法,包括 yield return 或 yield break 语句。
扩展方法的第一个参数不能有 in 修饰符,除非该参数是结构。
扩展方法的第一个参数,其中该参数是泛型类型(即使该类型被约束为结构。)

二、实例化接口对象

接口不仅可以声明对象,而且可以把对象实例化,还可以当做参数被传入。

继承中的向上转型,父类FL = new 子类();只不过这里的父类就是interfa接口。

接口的实例化实际上是一个接口对象作为一个引用,指向是想了它方法的那个类中的所有方法。这一点非常像C++中的函数指针(在C#中类似委托),但是却是有,但是需要注意的是,接口对象的实例化必须用实现他的类来实例化。而不能用接口本身实例化。用接口本身实例化它自己的对象在C#中是不允许的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值