首页 > 编程语言 >C#学习笔记 -- 接口

C#学习笔记 -- 接口

时间:2023-06-27 10:57:14浏览次数:37  
标签:string C# void PrintOut IIfc1 接口 -- public

接口

1、什么是接口

  • 接口指定一组函数成员, 而不实现他们的引用类型

  • 只能类和结构来实现接口

例子
//声明实现接口的CA类
class CA : IInfo
{
    public string Name;
    public int Age;
​
    //在类中实现接口的方法
    public string GetAge()
    {
        return Age.ToString();
    }
​
    public string GetName()
    {
        return Name;
    }
}
class CB : IInfo
{
    public string First;
    public string Last;
    public double PersonsAge;
​
    public string GetAge()
    {
        return PersonsAge.ToString();
    }
​
    public string GetName()
    {
        return First + "·" + Last;
    }
}
public interface IInfo
{
    string GetName();
    string GetAge();
}
public static class Printer
{
    public static void PrintInfo(IInfo item)
    {
        Console.WriteLine($"名字: {item.GetName()}, 年龄: {item.GetAge()}");
    }
}
static void Main(string[] args)
{
    CA a = new CA() { Name = "John Doe", Age = 35 };
    CB b = new CB() { First = "James", Last = " Doe", PersonsAge = 33 };
    PrintInfo(a);
    PrintInfo(b);
}

(1)使用IComparable接口

  • Array.Sort()依赖于IComparable的接口

    public interface IComparable
    {
        int CompareTo(Object obj);
    }
  • CompareTo()方法返回值如下

    • 负数: 如果当前对象小于参数对象

    • 正数: 如果当前对象大于参数对象

    • 0: 两个对象相对

  • Sort()使用的算法是依赖与使用对象的CompareTo()来决定两个元素的次序, int类型实现了IComparable, 但是自定义类没有, 因此自定义类的数组无法使用Sort()

  • 让类实现IComparable接口, 让Sort方法可以用于自定义类型的对象

class MyClass : IComparable
{
    public int TheValue;
    //实现接口方法
    public int CompareTo(object obj)
    {
        MyClass mc = (MyClass)obj;
        return this.TheValue - mc.TheValue;
    }
}

(2)实现一个接口, 类和接口必须要做的事情

  • 在基类列表中列出接口名称

  • 为接口的每个成员提供实现

2、声明接口

声明接口重要事项

  • 接口可以使用任何访问修饰 public、protected、internal、private

  • 接口成员是隐式public的, 不允许用任何访问修饰符, 包括public

  • 接口声明不能包含以下成员

    • 数据成员 (字段)

    • 静态成员

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

    • 方法

    • 属性

    • 事件

    • 索引器

  • 这些函数成员的声明不能包含任何实现代码, 必须使用分号代替每一个成员声明的主体

  • 接口名称必须以大写的I开头

  • 与类和结构一样, 接口声明也可以分割成分部接口声明

访问修饰符 interface IMyInterface1
{
    //不能有字段和静态成员
    //成员不能用任何访问修饰符
    Type DoStuff(Type nVar1, Type lVar2);
}

3、实现接口

只有类和结构才能实现接口, 要实现接口, 必须有如下

  • 在基类列表中包括接口名称

  • 为每一个接口成员提供实现

注意
  1. 如果类实现了接口, 他必须实现接口所有成员

  2. 如果类派生自基类并实现了接口, 基类列表中的基类名称必须放在接口之前

class Derived : MyBaseClass, IIfc1, IIfc2
{
    //...
}

4、接口是引用类型

  • 不能直接通过类对象的成员访问接口

  • 可以通过把类对象引用强制转换为接口类型来获取指向接口的引用

  • 一旦有了接口的引用, 就可以使用.来调用接口的成员, 使用这个接口, 你不能调用不属于这个接口成员的类成员

class MyClass : IIfc1
{
    public void PrintOut(string s)
    {
        //...
    }
}
interface IIfc1
{
    void PrintOut(string s);
}
static void Main(string[] args)
{
    MyClass mc = new MyClass(); //创建类对象
    mc.PrintOut("object");//调用对象的实现方法
    
    IIfc1 ifc = (IIfc1)mc;//转换为接口类型的引用
    ifc.PrintOut("iterface");//调用接口方法
}

5、接口和as运算符

可以使用强制转换运算符来获取对象接口的引用, 另一个更好的方式是as运算符

如果将类对象引用强制转换为类未实现的接口的引用, 强制转换操作为抛出异常, 可以使用as运算符来避免这个问题

接口名称 接口引用 = 类对象引用 as 接口名称
  • 如果类实现了接口, 表达式返回指向接口的引用

  • 如果类没有实现接口, 表达式返回null而不是抛出异常

ILiveBirth b = a as ILiveBirth;
if(b != null)
{
    Console.WriteLine($"{b.BabyCalled()}");
}

6、实现多个接口

  • 类或结构可以实现任意数量的接口

  • 所有实现的接口必须列在基类列表中并以逗号分割(如果有基类名称, 则在其之后)

class MyData : IDataRetrieve, IDataStore
{
    ...
}

7、实现有重复成员的接口

由于类可以实现任意数量的接口, 有可能有两个或多个接口成员具有相同的签名和返回类型

interface IIfc1
{
    void PrintOut(string s);
}
interface IIfc2
{
    void PrintOut(string s);
}
  • 如果一个类实现了多个接口, 并且其中一些接口成员具有相同的签名和返回类型

  • 那么类可以实现单个成员来满足所有包含重复成员的接口

class MyClass : IIfc1, IIfc2
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"PrintOut:{s}");
    }
}
static void Main(string[] args)
{
    MyClass mc = new MyClass();
    mc.Print("object");
}

8、多个接口的引用

  • 可以通过对象强制转换为接口类型的引用, 来获取一个指向接口的引用

  • 如果类实现了多个接口, 我们可以获取每个接口的独立引用

interface IIfc1
{
    void PrintOut(string s);
}
interface IIfc2
{
    void PrintOut(string s);
}
class MyClass : IIfc1, IIfc2
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"s = {s}");
    }
}
static void Main(string[] args)
{
    MyClass mc = new MyClass();
    IIfc1 ifc1 = (IIfc1) mc;
    IIfc2 ifc2 = (IIfc2) mc;

    mc.PrintOut("object");
    ifc1.PrintOut("Interface1");
    ifc2.PrintOut("Interface2");
}

9、派生成员作为实现

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

interface IIfc1
{
    void PrintOut(string s);
}
class MyBaseClass
{
    public void PrintOut(string s)
    {
        Console.WriteLine($"{s}");
    }
}
class Derived : MyBaseClass, IIfc1
{

}
static void Main(string[] args)
{
    Derived derived = new Derived();
    derived.PrintOut("derived");//dervied
}
  • 父类实现了某个接口的方法

  • 子类继承父类, 并且实现接口

  • 子类中不实现接口方法, 就可以调用

10、显示接口成员实现

  • 为每一个接口分离实现, 可以创建显示接口成员

  • 与所有接口实现想死, 位于实现了接口的类或结构中

  • 使用限定接口名称来声明, 由接口名称和成员名称以及他们中间的点来分隔符号构成

访问修饰符 返回类型 接口.方法(参数列表)
{
    实现
}
interface IIfc1
{
    void PrintOut(string t);
}
interface IIfc2
{
    void PrintOut(string s);
}
class MyClass1610 : IIfc1, IIfc2
{
    void IIfc1.PrintOut(string t)
    {
        Console.WriteLine($"IIfc1: {t}");
    }
    void IIfc2.PrintOut(string s)
    {
        Console.WriteLine($"IIfc2: {s}");
    }
}
static void Main(string[] args)
{
    MyClass1610 mc = new MyClass1610();
    Interface1610.IIfc1 iifc1 = (Interface1610.IIfc1) mc;
    iifc1.PrintOut("iifc1");

    Interface1610.IIfc2 iifc2 = (Interface1610.IIfc2) mc;
    iifc1.PrintOut("iifc2");

}
注意
  1. 在接口方法没有指向类级别实现, 而是包含了自己的代码

  2. 不能使用MyClass1610的对象来调用方法, 不存在类级别的Printout方法

如果有显示接口成员实现, 类级别的实现是允许的, 担不是必须的, 显示实现满足了类火结构必须实现方法的需求, 因此, 可以有如下三种实现场景

  • 类级别实现

  • 显示接口成员实现

  • 类级别和显示接口成员实现

访问接口成员实现

显示接口成员只能通过接口指向的引用来访问, 其他的类成员都不可用直接访问他们, 必须转换为接口引用

class MyClass1610 : IIfc1
{
    void IIfc1.PrintOut(string t)
    {
        Console.WriteLine($"IIfc1: {t}");
    }
    
    public void Method1()
    {
        //编译错误
        //PrintOut("x");
        //this.PrintOut("x");
        //转换为接口引用
        ((IIfc1)this).PrintOut("xxx");

    }
}

这个限制对继承产生类重要的影响, 由于其他类成员不能直接访问显示接口成员实现, 派生类的成员也不能直接访问他们

11、接口可以继承接口

  • 要指定某个接口继承其他接口, 形式如下

    interface 派生接口 : 基接口1, 基接口2
    {
        ...
    }
  • 类的基类只能有一个, 而接口可以有多个任意基接口

    • 列表中的接口本身可以继承其他接口

    • 结果接口包含它声明的所有成员和基接口的所有成员

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

}
class MyData : IData
{
    int nPrivateData;
    public int GetData()
    {
        return nPrivateData;
    }

    public void SetData(int x)
    {
        nPrivateData = x;
    }
}
static void Main(string[] args)
{
    MyData myData = new MyData();
    myData.SetData(5);
    Console.WriteLine($"{myData.GetData()}");
}

12、不同类实现一个接口

interface ILiveBirth
{
    string BabyCalled();
}
class Animal
{

}
class Dog : Animal, ILiveBirth
{
    public string BabyCalled()
    {
        return "puppy";
    }
}
class Cat : Animal, ILiveBirth
{
    public string BabyCalled()
    {
        return "pussy";
    }
}

标签:string,C#,void,PrintOut,IIfc1,接口,--,public
From: https://www.cnblogs.com/phonk/p/17508081.html

相关文章

  • jmeter测试tcp接口
    Jmeter测试tcp接口一.tcp接口结构Tcp接口:只有ip和port传参数据类型根据开发定义的类型需要添加一个ascii码的十进制字节结束符,tcp才会关闭二.Tcp接口环境搭建在testfan-tcp-server.jar目录下,启动cmd命令行,输入:java-jartestfan-tcp-server.jar三.Jmeter测试tcp接口1.添加tc......
  • 003.clickhouse-ck输出所有的表结构
    --clickhouse输出库中表结构--输出所有表名clickhouseclient--host1xx.xxx.xx.xx--userxx_xx--passwordxxxx--multiquery--query="usedefault;showtables;">default_table.txtvimecho_table_ck.sh--输出所有表结构#!/bin/bashecho'usedefault;&......
  • jmeter测试签名接口
    Jmeter测试签名接口一.了解签名规则1.常见的签名规则:业务数据+当前时间戳2.签名方式:md5(大部分)二.接口信息1.Ip和端口号:10.196.104:80802.接口路径:/pinter/com/userInfo3.请求类型:post4.数据类型:json5.请求参数:{ "phoneNum":"123434", "optCode":"testfan", "timestamp":"......
  • 在pod内,如何通过curl命令来解析一个域名的地址?
    最近在测试kubernetes的这个apiserver的域名解析出来的地址,到底是多少,然后呢,就进入到一个容器里面,发现没有ping、nslookup、host、dig、nc、wget命令 如何知道这个service名字是解析到了什么呢? 因为在pod内,使用coredns解析,就是解析service的名字 后来查了以下,可以通过c......
  • react的函数式组件中使用ref获取到子组件的方法为undefined
    我暂时遇到了两种情况。第一种情况:useImperativeHandle函数写错useImperativeHandle的第二个参数的返回值是作为ref.current的值,但是我写箭头函数写快了,忘记返回值了。useImperativeHandle(ref,()=>{foo},[foo])//错误,这样没有返回值,所以ref.current为undefineduseImp......
  • jmeter中跨线程调用变量
    Jmeter中跨线程调用变量一.Jmeter中线程运行规则1.各个线程组是完全独立的,每个线程组是不同的业务,互不影响2.线程组中的每个线程也是完全独立3.线程组中的每个线程,都是从上往下执行,完成一轮循环后,继续下一轮循环4.存在业务流或者接口之前存在依赖关系的放同一个线程组5.setup......
  • Vue如何在页面加载时将url的参数赋值给组件
    <template> <inputv-model="loginForm.username" name="username" type="text" tabindex="1" auto-complete="on" /><inputv-model="loginForm.password":type="passwordType"......
  • 插件qrcode生成二维码
    1、安装依赖[email protected]、引入importQrCodefrom'qrcode'3、使用constcontent=window.location?.href||''QrCode.toDataURL(content,{margin:1,widht:128,height:1......
  • Linux扩展篇-shell编程(五)-流程控制(二)-case语句
    基本语法:case"${item}"in1)echo"item=1";;2|3)echo"item=2oritem=3";;*)echo"default(noneofabove)";;esac注意事项:以case开始esac结尾case行尾必须为单词“in”,每......
  • Java跟数据库连接有问题怎么办?
    Java与数据库连接出现问题时,可以尝试以下几个步骤来解决问题:1、检查数据库驱动程序:确保使用的数据库驱动程序与数据库版本兼容。检查并确保已正确导入所需的数据库驱动程序的jar文件。2、验证数据库连接参数:确保提供了正确的数据库连接参数,包括数据库URL、用户名和密码等。验证......