C#协变接口、逆变接口

不变量


        using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace ConsoleApp2
    {
        interface IWrapper<T>
        {
            void SetData(T data);
            T GetData();
        }
        class Wrapper<T> : IWrapper<T>
        {
            private T storedData;

            void IWrapper<T>.SetData(T data)
            {
                this.storedData = data;
            }

            //显式实现接口,调用时只能通过接口引用来进行调用
            T IWrapper<T>.GetData()
            {
                return this.storedData;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Wrapper<string> stringWrapper = new Wrapper<string>();
                //这句话编译没问题,但是在执行的时候会抛出异常
                //无法执行强转
                IWrapper<object> storedStringWrapper = (IWrapper<object>)stringWrapper;
                storedStringWrapper.SetData("hello");
                Console.WriteLine(storedStringWrapper.GetData());
            }
        }
    }

上面这段代码中的IWrapper接口是不变量,也就是说,我们不能将IWrapper对象赋给IWrapper类型的引用,就算A由B派生

协变接口

先看下面的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Test
    {
        private int day { set; get; }
        public void Display()
        {
            Console.WriteLine("SSS");
        }
    }

    interface IStoreWrapper<T>
    {
        void SetData(T data);
    }

    interface IWrapper<out T>
    {
        T GetData();
    }
    //struct T
    //{
    //    public int X { get; set; }
    //    public int Y { get; set; }
    //}
    class Wrapper<T> : IWrapper<T>, IStoreWrapper<T>
    {
        private T storedData;

        void IStoreWrapper<T>.SetData(T data)
        {
            this.storedData = data;
        }

        //显式实现接口,调用时只能通过接口引用来进行调用
        T IWrapper<T>.GetData()
        {
            return this.storedData;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Wrapper<string> stringWrapper = new Wrapper<string>();
            IStoreWrapper<string> setStringWrapper = stringWrapper;
            //IWrapper接口只有一个GetData方法,不会对数据进行任何修改
            //对于泛型接口定义的方法,如果类型参数T仅在方法的返回值中出现
            //可通过out关键字来告诉编译器,一些隐式转换是合法的
            //可以转换成其他类型,但是这个转换是有条件的,这里我随便创建了一个Test类
            //这样是无法编译成功的,就那这里的借口说吧,现在我要把Wrapper<A>对象赋给
            //IWrapper<B>引用,那么B一定要是A的基类,就是说A一定要派生自B
            IWrapper<Test> getStringWrapper = stringWrapper;
            setStringWrapper.SetData("hello");
            Console.WriteLine(getStringWrapper.GetData());
        }
    }
}

在上面的代码中,我们就可以进行隐式类型转换了
因为IWrapper接口只有一个GetData方法,不会对数据进行任何修改,对于泛型接口定义的方法,如果类型参数T仅在方法的返回值中出现,可通过out关键字来告诉编译器,一些隐式转换是合法的
C#中的这个功能,就叫做协变性,拥有这种功能的接口,就是协变接口

逆变接口

有协变接口,那么自然就有逆变接口,它允许使用泛型接口通过A类型(比如String)的一个引用来引用B类型(比如Object)的一个对象,只要A从B派生

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class ObjectComparer : IComparer<Object>
    {
        int IComparer<Object>.Compare(Object x, Object y)
        {
            int xHash = x.GetHashCode();
            int yHash = y.GetHashCode();
            if (xHash == yHash)
                return 0;
            if (xHash < yHash)
                return -1;
            return 1;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Object x = 1;
            Object y = 2;
            ObjectComparer objectComparer = new ObjectComparer();
            IComparer<Object> iComparer1 = objectComparer;
            Console.WriteLine(iComparer1.Compare(x, y));
            IComparer<String> iComparer2 = objectComparer;
            Console.WriteLine(iComparer2.Compare("asd", "dsa"));
        }
    }
}

上面代码中的IComparer接口就是具有逆变性的接口,IComparer<string>可以和IComparer<object>引用同一个对象,因为在IComparer接口的实现中:

namespace System.Collections.Generic
{
    public interface IComparer<in T>
    {
        int Compare(T x, T y);
    }
}

IComparer的类型参数中有in关键字,这样一来,IComparer<string>就可以引用IComparer<object>类型的对象,因为string是object的一个派生类,只要前者的派生程度高于后者就可进行这种操作

猜你喜欢

转载自blog.csdn.net/include_heqile/article/details/80977806