IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity3D数字孪生开发笔记——C#运算符重载/接口篇 -> 正文阅读

[游戏开发]Unity3D数字孪生开发笔记——C#运算符重载/接口篇

一、运算符重载

  • 重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。 与其他函数一样,重载运算符有返回类型和参数列表。
  • 语法:
    public ? ~ ?static ? ~ ?返回值类型 ? ~ ?operator ? ~ ?一元运算符 ? ~ ?(参数类型 ? ~ ? 参数名)
    public ? ~ ?static ? ~ ?返回值类型 ? ~ ?operator ? ~ ?二元运算符 ? ~ ?(参数1类型 ? ~ ? 参数1名,参数2类型 ? ~ ? 参数2名)

1、可重载和不可重载运算符

在这里插入图片描述

using System;

namespace Day10
{
    public class Fraction
    {
        private int num1;
        private int num2;

        public Fraction (int num1,int num2)
        {
            this.num1 = num1;
            this.num2 = num2;
        }

        public static Fraction operator +(Fraction f1,Fraction f2)//重载+法
        {
            return new Fraction(f1.num1 + f2.num1, f1.num2 + f2.num2);
        }

        public static bool operator ==(Fraction f1,Fraction f2)
        {
            if (f1.num1 == f2.num1 && f1.num2 == f2.num2)
                return true;
            return false;
        }

        //委托给==
        public static bool operator !=(Fraction f1,Fraction f2)
        {
            return !(f1 == f2);
        }

        //判断是否为同一类型,再执行委托
        public override bool Equals(object obj)
        {
            if (!(obj is Fraction))  //检查对象运行时类型
                return false;
            return this == (Fraction)obj;//比较this == obj ,比较前先将obj强制转换为Fraction
        }

        public override string ToString()//复写Tostring
        {
            return String.Format("({0},{1})", num1, num2);
        }
    }

    public  class Tester
    {
        public void Run()
        {
            Fraction f1 = new Fraction(3, 4);
            Console.WriteLine("f1:{0}",f1.ToString());
            Fraction f2 = new Fraction(5, 6);
            Console.WriteLine("f2:{0}",f2.ToString());
            Fraction f3 = f1 + f2;
            Console.WriteLine("f3:{0}",f3.ToString());
            Fraction f4 = new Fraction(8, 10);
            if(f1==f2)
                Console.WriteLine("f1:{0}==f2:{1}",f1.ToString(),f2.ToString());
            else
                Console.WriteLine("f1:{0}!=f2:{1}", f1.ToString(), f2.ToString());
            if(f4.Equals(f3))
                Console.WriteLine("f4:{0}Equalsf3:{1}",f4.ToString(),f3.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Tester t = new Tester();
            t.Run();
        }
    }
}

注意:
1、如果重载了等于(==)运算符,那么也必须重载不等(!=)运算符,类似地有小于(<)和大于(>)必须成对,小于等于(<=)和大于等于(>=)必须成对。
2、如果重载了等于运算符,那么建议也要重载Equals()方法。
3、比较运算符必须返回bool类型的值,这是与其他算术运算符的根本区别。
4、运算符只能采用值参数,不能采用ref 或out 参数。

2、转换运算符

  • implicit 和 explicit通常叫作转换运算符,当转换能够成功并且不会发生信息丢失时,使用关键字implicit;如果存在丢失信息的风险,则需要使用关键字explicit。
  • 语法:
    public ? ~ ?static ? ~ ?implicit ? ~ ?operator ? ~ ?转换运算符的目标类型 ? ~ ?(参数输入类型 ? ~ ? 参数名)
    public ? ~ ?static ? ~ ?explicit ? ~ ?operator ? ~ ?转换运算符的目标类型 ? ~ ?(参数输入类型 ? ~ ? 参数名)
using System;

namespace Day10
{
    public class Fraction
    {
        private int num1;
        private int num2;

        public Fraction (int num1,int num2)
        {
            this.num1 = num1;
            this.num2 = num2;
        }
        //重载构造函数
        public Fraction (int num)
        {
            this.num1 = num;
            this.num2 = 1;
        }

        //隐式转换整型为Fraction类型,不会发生信息丢失,因此使用implicit
        public static implicit operator Fraction(int num3)
        {
            return new Fraction(num3);
        }

        //明确(强制)转换Fraction类型为整型,会产生截断值,因此使用explicit
        public static explicit operator int(Fraction f1)
        {
            return f1.num1 / f1.num2;
        }

        //重载+法
        public static Fraction operator +(Fraction f1,Fraction f2)
        {
            return new Fraction(f1.num1 + f2.num1, f1.num2 + f2.num2);
        }
        //重载==
        public static bool operator ==(Fraction f1,Fraction f2)
        {
            if (f1.num1 == f2.num1 && f1.num2 == f2.num2)
                return true;
            return false;
        }

        //委托给==
        public static bool operator !=(Fraction f1,Fraction f2)
        {
            return !(f1 == f2);
        }

        //判断是否为同一类型,再执行委托
        public override bool Equals(object obj)
        {
            if (!(obj is Fraction))  //检查对象运行时类型
                return false;
            return this == (Fraction)obj;//比较this == obj ,比较前先将obj强制转换为Fraction
        }

        public override string ToString()//复写Tostring
        {
            return String.Format("({0},{1})", num1, num2);
        }
    }

    public  class Tester
    {
        public void Run()
        {
            Fraction f1 = new Fraction(3, 4);
            Console.WriteLine("f1:{0}",f1.ToString());
            Fraction f2 = new Fraction(5, 6);
            Console.WriteLine("f2:{0}",f2.ToString());
            Fraction f3 = f1 + f2;
            Console.WriteLine("f3:{0}",f3.ToString());
            Fraction f4 = new Fraction(8, 10);
            Fraction f5 = f4 + 4;
            Console.WriteLine("f5:{0}",f5.ToString());
            int num = (int)f5;
            Console.WriteLine("num:{0}",num);
            if(f1==f2)
                Console.WriteLine("f1:{0}==f2:{1}",f1.ToString(),f2.ToString());
            else
                Console.WriteLine("f1:{0}!=f2:{1}", f1.ToString(), f2.ToString());
            if(f4.Equals(f3))
                Console.WriteLine("f4:{0}Equalsf3:{1}",f4.ToString(),f3.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Tester t = new Tester();
            t.Run();
        }
    }
}

二、接口

  • 接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明,接口没有构造函数,所以不能直接使用 new 对接口进行实例化,成员的定义是派生类的责任。

  • 接口和抽象类的区别:
    1、C#类只能够从一个单独的父类中继承,但能够实现多个接口,即类只能单继承,而接口可以实现跨继承(多继承)
    2、当派生自一个抽象类时,必须重写抽象类中的所有抽象方法;实现接口时,接口中定义的每一个方法都必须实现,不能部分地实现一个接口;
    3、类的继承体现的是is-a(是一个,属于)关系,接口的实现体现的是able(能够)关系;

  • 接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的,通常接口命令以字母 I 开头,
    ----语法:
    interface 接口名
    {
    //接口成员
    }

  • 接口的成员不能有 protected、internal、private 、new、static、abstract、override、virtual等修饰符。

using System;
using System.Collections.Generic;
using System.Text;

namespace Day10
{
    interface IFlyable
    {
       public void Fly();
    }

    abstract class Animal
    {
        public string Name { get; set; }

        public Animal(string name) { this.Name = Name; }

        public abstract void Shout();

        //public abstract void Fly();
    }

     class Dog : Animal
    {
        public Dog(string name) : base(name) { }

        public override void Shout()
        {
            Console.WriteLine("{0}:汪汪",this.Name); 
        }

        //public override void Fly()
        //{
        //    Console.WriteLine("狗不能飞!");
        //}
    }

    abstract  class Vehicle
    {

    }

    class Car:Vehicle
    {

    }

    class Plane:Vehicle,IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("飞机正在飞!");
        }
    }

    class Cat : Animal
    {
        public Cat(string name) : base(name) { }

        public override void Shout()
        {
            Console.WriteLine("{0}:喵喵", this.Name);
        }

        //public override void Fly()
        //{
        //    Console.WriteLine("猫不能飞!");
        //}
    }

    class Duck : Animal,IFlyable
    {
        public Duck(string name) : base(name) { }

        public override void Shout()
        {
            Console.WriteLine("{0}:嘎嘎", this.Name);
        }

        public void Fly()
        {
            Console.WriteLine("鸭子正在飞!");
        }
    }

    class Bird : Animal,IFlyable
    {
        public Bird(string name) : base(name) { }

        public override void Shout()
        {
            Console.WriteLine("{0}:叽叽喳喳", this.Name);
        }
        public void Fly()
        {
            Console.WriteLine("鸟儿飞来飞去!");
        }
    }

    class program
    {
        static void IWant2Fly(IFlyable fly)
        {
            fly.Fly();
        }

        static void Main()
        {
            IWant2Fly(new Duck("小黑"));
        }
    }
}

  • 实现多接口的语法:
    访问修饰符 ? ~ ?class ? ~ ?类名:(父类,)接口1,接口2,接口3…
    注意: 使用多接口时,每个接口中的方法也都必须实现。

1、is 和 as 运算符

  • is运算符可以查询一个对象是否实现了某个接口(或派生自某个基类),运算符形式为:
    if(expression is type)
    如果表达式(必须为引用类型,如一个类的实例)能够被安全转换为要转换的类型,而不抛出异常,运算符结果为true。
  • as 运算符比is运算符多执行一步,它尝试将对象转换为想要转换的类型,并且转换过程如果抛出异常,as运算符返回null。
  • 注意:is运算符比as运算符的功能略微弱一点,因此处理只希望做检测而实际不做转换的情况,其他情况都首选as运算符而不是is运算符。
using System;
using System.Collections.Generic;
using System.Text;

namespace Day10
{
    interface IStorable
    {
        void Read();
        void Write(object obj);
        int Status { get; set; }
    }

    interface ICompressible
    {
        void Compress();
        void Decompress();
    }


   public class Note:IStorable
    {
        private string str;

        public int Status { get ; set; }

        public Note (string str)
        {
            this.str = str;
        }

        public override string ToString()
        {
            return str;
        }

        public void Read()
        {
            Console.WriteLine("为IStorable接口执行Note类中的Read方法");
        }

        public void Write(object obj)
        {
            Console.WriteLine("为IStorable接口执行Note类中的Write方法");
        }

    }
   public class Document:Note,ICompressible
    {
        private int documentID;
        public int ID { get { return this.documentID; } }
        public Document(string str,int documentID):base(str)
        {
            this.documentID = documentID;
        }

        public void Compress()
        {
            Console.WriteLine("为ICompressible接口执行Document类中的Compress方法");
        }

        public void Decompress()
        {
            Console.WriteLine("为ICompressible接口执行Document类中的DeCompress方法");
        }
    }

    public class Tester
    {
        public void Run()
        {
            string str = "string";
            Note[] myNoteArray = new Note[3];

            for (int i = 0; i < 3; i++)
            {
                string docText = str + i.ToString();
                if(i%2==0)
                {
                    Document myDocument = new Document(docText, (i+1)*10);
                    myNoteArray[i] = myDocument;
                }
                else
                {
                    Note myNote = new Note(docText);
                    myNoteArray[i] = myNote;
                }
            }
            //用is运算符检测Note是否能够安全赋值给一个ICompressible引用
            foreach (var theID in myNoteArray)
            {
                Console.WriteLine("\nTesting {0} with IS",theID);
                theID.Read(); 
                if(theID is ICompressible)
                {
                    ICompressible myCompressible = theID as ICompressible;
                    myCompressible.Compress();
                }
                else
                {
                    Console.WriteLine("This is not compressible!");
                }

                if(theID is Document)
                {
                    Document myDoc = theID as Document;

                    //清除转换
                    myDoc = theID as Document;
                    Console.WriteLine("my document ID is{0}",myDoc.ID);
                }
            }
            //用as运算符检测Note是否能够安全赋值给一个ICompressible引用
            foreach (var theID in myNoteArray)
            {
                Console.WriteLine("\nTesting {0} with AS",theID);
                ICompressible myCompressible = theID as ICompressible;
                if(myCompressible!=null)
                {
                    myCompressible.Compress();
                }
                else
                {
                    Console.WriteLine("This is not compressible!");
                }

                Document myDoc = theID as Document;
                if(myDoc!=null)
                {
                    Console.WriteLine("my document ID is{0}",((Document)theID).ID);
                    //额外的括号确保在访问属性前已经完成了转换
                    //Console.WriteLine("my document ID is{0}",myDoc.ID);
                }
                else
                {
                    Console.WriteLine("this is not a document!");
                }
            }
        }

        static void Main()
        {
            Tester test = new Tester();
            test.Run();
        }
    }

}

2、重载接口方法

  • 扩展接口以及合并接口都可以视为是接口的继承;
  • 父类引用,子类对象:运行和编译时,成员变量和方法调用情况:
    在这里插入图片描述
    以下代码为上图的解释:
using System;
using System.Collections.Generic;
using System.Text;

namespace Day10
{
    class Father
    {
        public int a = 10;
        public string str = "父类";
        public virtual void Run()
        {
            Console.WriteLine("这是父类");
        }
    }

    class Son:Father
    {
        public int a = 5;
        public string str = "子类";
        public override void Run()
        {
            Console.WriteLine("这是子类");
        }
        public void Stop()
        {
            Console.WriteLine("子类停止");
        }
    }

    class Test
    {
        static void Main()
        {
            Father X = new Son();
            Console.WriteLine("X.a={0}",X.a);  //10
            Console.WriteLine("X.str={0}",X.str);  //父类
            X.Run();   //这是子类
            //X.Stop();  //编译报错
        }
    }
}

  • 关于重载接口的代码如下:
using System;
using System.Collections.Generic;
using System.Text;

namespace Day11
{
    interface IStorable
    {
        void Read();
        void Write();
    }

    public class Note : IStorable
    {

        public Note(string str)
        {
            Console.WriteLine("creating note with:{0}",str);
        }
        //重载接口方法,同时利用虚函数实现多态
        public virtual void Read()
        {
            Console.WriteLine("为IStorable接口执行父类中的Read方法");
        }
        //重载接口方法,非虚函数
        public void Write()
        {
            Console.WriteLine("为IStorable接口执行父类中的Write方法");
        }

    }
    public class Document : Note
    {
 
        public Document(string str) : base(str)
        {
            Console.WriteLine("creating document with:{0}",str);
        }
        //重载父类的虚方法
        public override void Read()
        {
            Console.WriteLine("Override执行子类中的Read方法");
        }
        //重写新的、属于自己的Write函数
        public new void Write()
        {
            Console.WriteLine("执行子类中的新的Write方法");
        }
    }
    class Chongzai
    {
        public void Run()
        {
            //通过父类引用子类对象
            Note theNote = new Document("父类引用,子类对象");
            theNote.Read();
            theNote.Write();
            Console.WriteLine("\n");
            //通过接口父类创建的接口引用子类对象
            IStorable isStorable1 = theNote as IStorable;//将父类引用--》接口引用(子类对象并未改变)
            if(isStorable1!=null)
            {
                isStorable1.Read();//接口引用仍然指向父类,但因为是子类对象,实现时因为多态所以执行子类的方法
                isStorable1.Write();
            }
            Console.WriteLine("\n");
            //子类引用,子类对象
            Document theDoc = new Document("子类引用,子类对象");
            theDoc.Read();
            theDoc.Write();
            Console.WriteLine("\n");
            //子类对象创建的接口
            IStorable isStorable2 = theDoc as IStorable;
            if(isStorable2!=null)
            {
                isStorable2.Read();//虽然继承了父类的接口,但是子类并没有实现接口,但实现了多态
                isStorable2.Write();//调用的是父类的方法,因为子类的write并不是实现接口的重写或多态
            }
        }

        static void Main()
        {
            Chongzai test = new Chongzai();
            test.Run();
        }
    }
}

在这里插入图片描述
以上代码需要着重理解,这里面涉及了在接口中实现多态,实际中可能运行不多。

3、显示接口实现

  • 当多个接口具有相同的名称时,显示实现能够避免冲突。
using System;
using System.Collections.Generic;
using System.Text;

namespace Day12
{
    interface IStorable
    {
        void Read();
        void Write();
    }

    interface ITalk
    {
        void Read();
        void Talk();
    }

    class Document:IStorable,ITalk
    {
        public Document (string s)
        {
            Console.WriteLine("creating document with :{0}",s);
        }
        //隐式实现
        public virtual void Read()
        {
            Console.WriteLine("document read for istorable");
        }

        public void Write()
        {
            Console.WriteLine("document write for istorable");
        }

        //显示实现     --》        接口名.方法()
        void ITalk.Read()
        {
            Console.WriteLine("implementing Italk.read");
        }

        public void Talk()
        {
            Console.WriteLine("implementing Italk.talk");
        }
    }

    class Tester
    {
        public void Run()
        {
            Document doc = new Document("test document");
            IStorable istorable = doc as IStorable;
            if(istorable!=null)
            {
                istorable.Read();
            }

            ITalk italk = doc as ITalk;
            if(italk!=null)
            {
                italk.Read();
            }

            doc.Read();
            doc.Talk();
        }

        static void Main()
        {
            Tester test = new Tester();
            test.Run();
        }
    }
}

三、小结

  • 接口是一个契约,类通过这个契约可以保证实现确定的方法,提供确定的属性和索引并支持确定的事件,所有这些内容都在接口定义中描述;
  • 不能定义一个接口的实例,要访问接口方法,需要创建一个实现了该接口的类;
  • 声明一个接口与声明类相似,但使用关键字interface;
  • 为了在一个类中实现接口,需要使用冒号运算符,冒号运算符之后是接口的名字,这个语法与继承的语法类似;
  • 类只能够从一个类派生,但能够实现多个接口。如果一个类有一个基类和一个或多个接口,那么积累必须放在第一个位置(冒号之后),基类以及各接口名字之间用逗号分隔;
  • 当定义一个实现了接口的类时,类必须实现接口要求的所有成员;
  • is运算符能够判断一个对象是派生自某个类或实现了某个接口。is运算符返回一个类型转换能否成功的bool值,但不执行这个类型转换;
  • as运算符尝试将一个引用转换为某一个基类型或接口,如果类型转换不成功则返回空;
  • 通过添加新方法或成员能够扩展一个接口。在新接口定义中,使用冒号运算符,之后是原始接口的名称,这与类之间的派生非常相似;
  • 扩展接口包含原始接口在,因此实现扩展接口的任何类必须也同样实现原始接口;
  • 实现接口的类可以将接口中的方法标识为virtual,然后这些方法可以在派生类中被重载,实现多态;
  • 当一个类实现两个或多个接口,并且在不同接口中有相同名称的方法时,为了解决冲突,可以在方法名称前加接口名称和点运算符。如果这样做了就不能使用访问修饰符,这些方法隐含为公有方法。
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-30 12:48:10  更:2021-10-30 12:48:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 0:49:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码