• C#之接口


    声明接口

    接口声明只能包含如下类型的非静态成员函数:

    • 方法
    • 属性
    • 事件
    • 索引器

    接口的访问性与接口成员的访问性有一定的区别:

    • 接口声明可以有任何的访问修饰符:public,protected,internal,private
    • 然而,接口的成员是隐式public的,不允许有任何的访问修饰符,包括public,这意味着实现接口的类实例转换为接口后,接口可以调用接口所规定的方法

    接口是引用类型

    接口不仅仅是类或结构要实现的成员列表,它也是引用类型,不能通过直接通过类对象的成员访问接口,然而,可以通过把类对象引用强制转换为接口类型来获取指向接口的引用,一旦有了接口的引用,就可以使用点号来获取接口引用的实例。

    可以用as运算符来将实现接口的类转换为接口的引用,如果类没有实现接口,表达式返回null,而不是抛出异常。

    实现多个接口

    类或接口可以实现任意数量的接口,所有实现的接口必须列在基类列表中,并以逗号分隔。

    实现具有重复成员的接口

    由于类可以实现任意数量的接口,有可能两个或多个接口成员都有相同的签名和返回类型。如果一个类实现了多个接口,并且其中一些接口有相同签名和返回类型,那么类可以实现单个成员来满足所有包含重复成员的接口。如果类实现了多个接口,可以获取每一个接口的独立引用。

        interface IIfc1
        {
            void PrintOut(string s);
        }
        interface IIfc2
        {
            void PrintOut(string s);
        }
    
        class MyClass : IIfc1, IIfc2
        {
            public void PrintOut(string s)
            {
                Console.WriteLine("Calling through:{0}", s);
            }
        }
        class Program
        {
    
         public static void Main()
            {
                var mc = new MyClass();
                IIfc1 ifc1 = mc as IIfc1;
                IIfc2 ifc2 = mc as IIfc2;
    
                mc.PrintOut("object");
                ifc1.PrintOut("IIfc1");
                ifc2.PrintOut("IIfc2");
    
            }
    
        }
    

    image-20210919230621750

    派生成员作为实现

    实现接口的类可以从它的基类继承实现的代码。

    interface IIfc1P { void PrintingOUt(string s); }
        class MyBase
        {
            public void PrintingOUt(string s)
            {
                Console.WriteLine($"Mybasing calling{s}");
            }
        }
        class Derived : MyBase, IIfc1P
        {
    
        }
        class Program
        {
    
            public static void Main()
            {
    
                var deri = new Derived();
                IIfc1P iifc = (IIfc1P)deri;
                iifc.PrintingOUt("iifc1P");
    
            }
    

    image-20210919095549092

    显式接口成员实现

    单个类可以实现多个接口需要的所有成员(接口所指定的方法,属性,事件,索引器),尤其对于实现重复成员的接口,不想实现单个成员来满足这些包含重复成员的接口,想为每一个接口分离,该怎么做?在这种情况下,可以创建显式接口成员实现,它具有如下特性:

    • 与所有接口实现相似,位于实现了接口的类或结构中
    • 使用限定接口名称来声明,由接口名称和成员名称以及它们中间的点分隔符号构成
    • 使用了限定接口名称后,类要有自己的相同签名的成员,必须单独实现,但不是必需的。
    interface IIfc1 { void PrintOut(string s); }
        interface IIfc2{ void PrintOut(string s);}
        class MyClass : IIfc1, IIfc2
        {
            void IIfc1.PrintOut(string s)//限定接口名称
            {
                Console.WriteLine("IIfc1:{0}", s);
            }
            void IIfc2.PrintOut(string s)//限定接口名称
            {
                Console.WriteLine("IIfc2:{0}", s);
            }
            public void PrintOut(string s) //类单独实现
            {
                Console.WriteLine("MyClass:{0}", s);
            }
        }
        class Program
        {
            public static void Main()
            {
                var mc = new MyClass();
                IIfc1 iifc1 = (IIfc1)mc;
                IIfc2 iifc2 = (IIfc2)mc;
                iifc1.PrintOut("interface 1");
                iifc2.PrintOut("interface 2");
                mc.PrintOut("myclass");
    
            }
        }
    

    image-20210919103422184

    访问显示接口成员实现

    显式接口的成员实现只能通过指向接口的引用来访问,也就是说其他的类成员都不可以直接访问它们,当然派生类的成员也不能直接访问它,也必须通过指向接口的引用来访问

        interface IIfc1 { void PrintOut(string s); }
        class MyClass : IIfc1
        {
            void IIfc1.PrintOut(string s)
            {
                Console.WriteLine("IIFC1:{0}",s);
            }
    
            public void Method1()
            {
                //PrintOut("..."),不可以直接访问
                //this.PrintOut("..."), 不可以直接访问
                ((IIfc1)this).PrintOut("....");//必须转换为接口类型,通过接口来调用显式接口
            }
        }
        class Derived : MyClass
        {
            public void Method2()
            {
                ((IIfc1)this).PrintOut("this is derived");
            }
        }
        
        class Program
        {
            public static void Main()
            {
                var mc = new MyClass();
                mc.Method1();
                var dr = new Derived();
                dr.Method1();
                dr.Method2();
                var mcc = (MyClass)dr;
                mcc.Method1();
    
    
            }
        }
    

    image-20210919105603197

    接口可以继承接口

    interface IDataRetrieve{int GetData();}
    interface IDataStore{void SetData(int x);}
    interface IData:IDataRetrieve,IDataStore{}
    

    不同类实现一个接口的示例

        interface ILiveBirth { string BabyCalled(); }
        class Animal { }
        class Cat : Animal, ILiveBirth
        {
            string ILiveBirth.BabyCalled()
            {
                return "Kitten";
            }
        }
        class Dog : Animal, ILiveBirth
        {
            string ILiveBirth.BabyCalled()
            {
                return "Puppy";
            }
        }
    
        class Bird : Animal
        {
    
        }
        
        class Program
        {
         public static void Main()
            {
                Animal[] animalArray = new Animal[3];
                animalArray[0] = new Cat();
                animalArray[1] = new Bird();
                animalArray[2] = new Dog();
                foreach (var a in animalArray)
                {
                    if (a is ILiveBirth)
                    {
                        var b = a as ILiveBirth;
                        Console.WriteLine($"{b.BabyCalled()}");
                    }
                }
            }
    
    
            
        }
    

    image-20210919161504528

    泛型接口

    • 与泛型类的结合
    interface IMyIfc<T>
        {
            T ReturnIt(T invalue);
        }
        
        class Simple<S> : IMyIfc<S>
        {
            public S ReturnIt(S invalue)
            {
                return invalue;
            }
        }
        class Program
        {
         public static void Main()
            {
                var triInt = new Simple<int>();
                var triString = new Simple<string>();
                Console.WriteLine($"{triInt.ReturnIt(100)}");
                Console.WriteLine($"{triString.ReturnIt("Yes it is generic interface")}");
            }
    
    
            
        }
    

    image-20210919162905656

    • 实现不同类型参数的泛型接口是不同的接口,可以在非泛型类中实现泛型接口
        interface IMyIfc<T>
        {
            T ReturnIt(T inValue);
        }
    
        class Simple : IMyIfc<int>, IMyIfc<string>
        {
            public int ReturnIt(int test)
            {
                return test;
            }
            public string ReturnIt(string test) //只要签名与接口签名一致即可,也就是说函数参数名可以不必与接口规定的参数名一致
            {
                return test;
            }
        }
        class Program
        {
         public static void Main()
            {
                var trivial = new Simple();
                Console.WriteLine($"{trivial.ReturnIt(100)},and {trivial.ReturnIt("this is also generic interface")}");
            }
    

    image-20210919163508803

    • 泛型接口的实现必须唯一

    实现泛型接口时,必须保证类型实参组合不会在类型中产生两个重复的接口。

    ​ 错误一

    image-20210919164914824

    泛型类中传入类型参数,泛型接口才可以使用该类型参数,否则就会出现上述错误

    更正后,又一错误,即类型实参组合会在类型中产生两个重复的接口。

    image-20210919165120104

    接口的协变与逆变

    class Animal
        { public string Name;  
        }
    class Dog : Animal { }
        
        interface IMyIfc<out T> //协变关键字out,也规定了必须返回T类型
        {
            T GetFirst();
        }
    
        interface IMyIfc2<in T> //逆变关键字in,也规定了必须以T类型为输入参数
        {
            void PrintOut(T invalue);
        }
        class Program
        {
            static void DoSomething(IMyIfc<Animal> returner) //以接口为输入参数,接口的类型参数为Animal
            {
                Console.WriteLine(returner.GetFirst().Name);//调用接口的GetFirst方法,为此,不得不将实现接口的类转换为接口
            }
    
            static void ToTest(IMyIfc2<Dog> dogtester)
            {
                var d = new Dog() { Name = "jack" };
                dogtester.PrintOut(d);
            }
    
         public static void Main()
            {
                //协变
                var dogReturner =new SimpleReturn<Dog>();
                dogReturner.items[0] = new Dog { Name = "Avonlea" };
                var animalReturner = dogReturner as IMyIfc<Animal>;
                Console.WriteLine(animalReturner.GetFirst().Name);
                DoSomething(animalReturner);
                
                //逆变
                var animalTest = new TestReturn();
                var dogTest = animalTest as IMyIfc2<Dog>;
                ToTest(dogTest);
    
                //协变
                var test2 = new TestReturn2();
                var ani = test2 as IMyIfc<Animal>;
                Console.WriteLine(ani.GetFirst().Name);
                
    
    
    
            }
    
        class SimpleReturn<T> : IMyIfc<T> //实现接口的类
            {
                public T[] items = new T[2];
                public T GetFirst()
                {
                    return items[0];
                }
            }
    
        class TestReturn : IMyIfc2<Animal>
            {
                
                public void PrintOut(Animal ani)
                {
                    Console.WriteLine("IMyIfc2:"+ani.Name);
                }
            }
    
        class TestReturn2 : IMyIfc<Dog>
            {
                Dog cuteDog = new Dog { Name = "Jack" };
                public Dog GetFirst()
                {
                    return cuteDog;
                }
            }
            
    
            
        }
    

    image-20210919220257188

    协变是以out为关键字,逆变以in为关键字。

    T0T1的基类,若记泛型类型T0的接口或者委托为F(T0),则

    协变就是可以将F(T1)转换为F(T0),因为协变是对输出有要求,如果期待是输出派生类T1,实际输出的是基类T0,是不会有问题的,也是类型安全的,即可以将F(T1)转换为F(T0)

    逆变就是可以将F(T0)转换为F(T1),因为逆变是对输入有要求,如果期待输入的是基类T0,实际输入的是派生类T1,是不会有问题的,也是类型安全的,即可以将F(T0)转换为F(T1)

    对于接口而言,由于调用接口的方法是必须将实现接口的类转换为接口,所以必须有实现泛型接口的类,然后可以将派生类转换为类型参数是基类的接口,此为协变,将基类转换为类型参数为派生类的接口,此为逆变

    口诀:out 协变,对输出有要求,派生类转基类;In逆变,对输入有要求,基类转派生类。

    ##### 愿你一寸一寸地攻城略地,一点一点地焕然一新 #####
  • 相关阅读:
    Python Generators(生成器)--yield
    [带你飞]一小时带你学会Python
    [Effective C++ --032]确定你的public继承塑模出is-a
    [Effective C++ --031]将文件间的编译依存关系降至最低
    [Effective C++ --030]透彻了解inlining的里里外外
    [Effective C++ --029]为“异常安全”而努力是值得的
    [Effective C++ --028]避免返回handles指向对象内部成分
    unity 获取本机ip地址
    unity 局域网游戏开发知识点
    unity 中函数调用的顺序
  • 原文地址:https://www.cnblogs.com/johnyang/p/15313118.html
Copyright © 2020-2023  润新知