unity的C#学习——属性、索引器和this关键字


C# 属性

在 C# 中,属性(Property)是一种特殊的成员,它允许我们通过访问器(Accessor)来访问一个类的私有字段(Private Field)。使用属性,我们可以将一个字段的值读取或者设置,而不用暴露字段本身。

  • 属性使得访问类成员更加简洁和安全。
  • 这里的字段指的是直接在类当中定义的变量,在Python中会将这些变量称为实例属性,但是注意C#中是不同的:
    • 字段定义时通常采用 private 访问修饰符,因此在类的外部通常是没法直接访问的,自然通过实例对象也没法直接获取字段值。
    • 属性通常采用 public 访问修饰符,因此通过实例对象可以直接访问属性,属性内部包含的访问器可以让我们获取和修改字段值。

1、定义属性

属性的定义包含以下几个部分:

  • 访问修饰符(Access Modifier):用来控制属性的访问级别,和字段的访问修饰符类似。
  • 数据类型(Data Type):属性的返回值类型,可以是任何有效的 C# 数据类型。
  • 属性名(Property Name):属性的名称,遵循 C# 命名规则。
  • 访问器(Accessor):属性的读取和写入方法,可以是 getset 访问器。

属性的定义语法如下:

[Access Modifier] [Data Type] [Property Name]
{
    get { /* get 访问器代码 */ }
    set { /* set 访问器代码 */ }
}

下面是一个例子,定义了一个 Person 类,包含 NameAge 两个属性:

class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }
}

在这个例子中,我们使用了 getset 访问器来读取和写入私有字段 nameage 的值。注意,访问器中的代码可以包含任何有效的 C# 语句,包括条件语句、循环语句等等。

在使用属性时:

  • 通过 实例名.属性名 会自动调用属性的 get 方法来获取属性的值
  • 如果对 实例名.属性名 进行赋值,则会自动调用 set 方法来设置属性的值

以下是一个示例代码,展示了如何使用 C# 中的属性:

public class Person
{
    private string _name;
    private int _age;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var person = new Person();
        person.Name = "Alice"; // 设置 Name 属性的值,调用 set 方法
        person.Age = 25; // 设置 Age 属性的值,调用 set 方法

        Console.WriteLine("Name: " + person.Name); // 获取 Name 属性的值,调用 get 方法
        Console.WriteLine("Age: " + person.Age); // 获取 Age 属性的值,调用 get 方法
    }
}

2、自动属性

在 C# 3.0 中,引入了自动属性(Auto-Implemented Properties),使得属性的定义更加简洁。使用自动属性,我们可以将属性的定义简化为一行代码:

[Access Modifier] [Data Type] [Property Name] { get; set; }

下面是使用自动属性的例子:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

在这个例子中,我们省略了访问器的具体实现,使用了自动属性来定义 NameAge 两个属性。编译器会自动为这两个属性生成一个私有字段,并且为它们生成默认的 get { return <私有字段名>; }set { <私有字段名> = value; } 访问器。


3、抽象属性

在 C# 中,抽象类可拥有抽象属性,这些属性应在派生类中被实现。需要注意自动属性抽象属性是两种不同类型的属性。它们之间有以下几个区别:

  • 实现方式:自动属性是通过编译器自动为属性生成 getset 方法的,而抽象属性需要在抽象类或接口中定义抽象 getset 方法(需要使用 abstract 关键字)。
  • 实现细节:自动属性的 getset 方法的实现是由编译器生成的,而抽象属性的 getset 方法需要在子类中进行实现
  • 使用场景:自动属性适用于简单的属性定义,而抽象属性通常用于定义接口或抽象类中的属性,以便在子类中进行实现。

以下是一个示例代码,展示了自动属性和抽象属性的使用:

// 自动属性示例
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// 抽象属性示例
public abstract class Animal
{
    public abstract string Name { get; set; }
    public abstract int Age { get; set; }
}

public class Dog : Animal
{
    public override string Name { get; set; } // 用自动属性实现了抽象属性
    public override int Age { get; set; } // 用自动属性实现了抽象属性
}

在上面的示例代码中,我们定义了一个 Person 类和一个抽象类 AnimalPerson 类中使用了自动属性来定义 NameAge 属性,而 Animal 抽象类中使用抽象属性来定义 NameAge 属性。在 Dog 类中,我们实现了抽象属性,以便子类对象使用。


4、只读属性和初始化器

有时候,我们希望定义一个只读的属性,即只能在对象的构造函数中初始化,不能在其他地方进行修改。为了实现这个目标,我们可以使用只读属性初始化器

  • 只读属性的定义方式和普通属性一样,只不过将 set 访问器删除
class Person
{
    public string Name { get; }
    public int Age { get; }
	// 构造函数,进行属性(对应私有字段)的初始化
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

在这个例子中,我们定义了 NameAge 两个只读属性,它们只能在构造函数中进行初始化。一旦对象被创建,它们的值就不能再进行修改。

  • 除了只读属性,还可以使用对象初始化器(Object Initializer)来为对象的属性赋初值。对象初始化器的语法如下:
new [Type]
{
    [Property1 = Value1],
    [Property2 = Value2],
    // ...
}

简单来说,就是在 new 创建对象的时候,通过添加一组“键值对”为每个属性(对应的私有字段)赋初值。下面是一个例子,使用对象初始化器为 Person 对象的 NameAge 两个属性赋初值:

var person = new Person
{
    Name = "Tom",
    Age = 20
};

C# this关键字

在C#中,this 关键字表示当前对象的实例,类似于Python中的 self 关键字,但是二者的使用有一定区别。this 关键字可以用于以下几种情况:

  1. 区分字段、局部变量和方法参数

在方法或构造函数中,如果存在一个局部变量或方法参数与类的字段同名,则可以使用 this 关键字来区分它们,如下所示:

class MyClass
{
    private int _value;

    public void SetValue(int value)
    {
        this._value = value; // 使用this关键字来区分字段和方法参数
    }
}

在上面的示例中,我们使用 this._value 来引用类的私有字段 _value,以区分方法参数 value

  1. 在构造函数中调用其他构造函数

在一个类的多个构造函数中,如果需要共享一些相同的初始化逻辑,可以使用 this 关键字来调用其他构造函数,如下所示:

class MyClass
{
    private int _value;

    public MyClass() : this(0) // 调用另一个构造函数来初始化_value
    {
    }

    public MyClass(int value)
    {
        this._value = value;
    }
}

在上面的示例中,我们定义了两个构造函数,其中一个调用了另一个构造函数来初始化类的字段。这样做可以避免重复代码。

  1. 对当前对象的引用

在C#中,当前对象的引用指的是关键字 this 所代表的对象。可以使用关键字 this访问当前对象的属性、方法或索引器。例如,可以使用 this.<propertyName> 来访问当前对象的属性,或者使用 this.<methodName>() 来调用当前对象的方法。

需要注意的是,当前对象的引用只在非静态方法和构造函数中才有意义,因为静态方法不依赖于任何对象实例。

此外,当前对象的引用也可以作为参数传递给其他方法,以便在方法内部使用该对象。

需要注意的是,在C#中对象作为参数传递时,默认情况下是按引用传递,而不是按值传递

以下是一个使用 this 关键字引用当前对象的示例代码:

public class Person
{
    private string name;
    private int age;

    public Person(string name, int age)
    {
        this.name = name; // 使用 this 关键字引用当前对象的 name 字段
        this.age = age; // 使用 this 关键字引用当前对象的 age 字段
    }

    public void PrintDetails()
    {
        Console.WriteLine("Name: {0}, Age: {1}", this.name, this.age); // 使用 this 关键字引用当前对象的 name 和 age 字段
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Person person = new Person("John", 30);
        person.PrintDetails(); // 输出 "Name: John, Age: 30"
    }
}

通过上面的代码我们可以发现,访问一个类的私有字段,除了使用属性中的访问器实现,还可以通过在公开的类方法中使用 this 关键字引用当前对象的字段来实现。


C# 索引器

在 C# 中,索引器(Indexer)是一个特殊的属性(Property),它允许对象像数组一样进行索引操作。在C#中,索引器使用 this 关键字来定义,并且可以像数组一样使用方括号[]来访问元素。


1、索引器的定义

索引器的声明在某种程度上类似于属性(property),我们可以使用 getset 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值

换句话说,它把实例数据分为更小的部分,并索引每个部分,获取或设置每个部分。

  • 定义一个属性(property)包括提供属性名称
  • 索引器定义的时候不带有名称,但带有 this 关键字,它指向对象实例
<access-modifier> <return type> this [index]
{
   // get 访问器
   get
   {
      // 返回 index 指定的值
   }

   // set 访问器
   set
   {
      // 设置 index 指定的值
   }
}

其中:

  • access-modifier 表示访问修饰符,例如public、private、protected等。
  • return type 表示索引器的返回类型。
  • index 表示索引器的参数,可以是一个或多个。参数类型可以是整数、字符串等任何类型。
  • getset 是访问器,用于获取和设置索引器的值。

下面是一个使用C#索引器的示例代码:

class MyClass
{
    private string[] _data = new string[10];

    public string this[int index]
    {
        get { return _data[index]; } // 获取当前类的实例对象的索引结果,访问私有字符串数组的索引结果
        set { _data[index] = value; }  // 为当前类的实例对象的索引位置赋值,访问私有字符串数组的索引位置
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyClass myClass = new MyClass();
        myClass[0] = "hello";
        myClass[1] = "world";
        Console.WriteLine(myClass[0]); // 输出:hello
        Console.WriteLine(myClass[1]); // 输出:world
    }
}

在上面的示例代码中,我们定义了一个名为MyClass的类,并在该类中定义了一个 this[int index] 索引器。该索引器接受一个整数参数 index,并使用一个私有成员变量 _data 来存储数据。在 get 访问器中,我们返回 _data[index] 的值;在 set 访问器中,我们设置 _data[index] 的值。

在Main方法中,我们创建了一个 MyClass 类的实例对象 myClass,并使用方括号 [] 来访问该对象的索引器。我们将字符串"hello"和"world"存储在了索引为0和1的位置上,并使用 Console.WriteLine 方法来输出它们的值。


2、重载索引器

索引器(Indexer)可被重载。下面的例子展示了通过不同的 index 参数的数据类型,实现索引器重载(与函数重载相同)

using System;

namespace IndexerApplication
{
    class IndexedNames
    {
        private string[] namelist = new string[size]; // 存储字符串的数组
        static public int size = 10; // 数组的大小为10

        public IndexedNames()
        {
            // 构造函数,初始化数组的元素为 "N. A."
            for (int i = 0; i < size; i++)
            {
                namelist[i] = "N. A.";
            }
        }

        // 读写索引器,用于按照整数索引数组元素
        public string this[int index]
        {
            get
            {
                string tmp;
                if (index >= 0 && index <= size - 1)
                {
                    tmp = namelist[index];
                }
                else
                {
                    tmp = "";
                }
                return tmp;
            }
            set
            {
                if (index >= 0 && index <= size - 1)
                {
                    namelist[index] = value;
                }
            }
        }

        // 只读索引器,用于按照字符串索引数组元素
        public int this[string name]
        {
            get
            {
                int index = 0;
                while (index < size)
                {
                    if (namelist[index] == name)
                    {
                        return index;
                    }
                    index++;
                }
                return index;
            }
        }

        static void Main(string[] args)
        {
            IndexedNames names = new IndexedNames();
            // 使用第一个索引器给数组元素赋值
            names[0] = "Zara";
            names[1] = "Riz";
            names[2] = "Nuha";
            names[3] = "Asif";
            names[4] = "Davinder";
            names[5] = "Sunil";
            names[6] = "Rubic";

            // 使用第一个索引器获取数组元素的值
            for (int i = 0; i < IndexedNames.size; i++)
            {
                Console.WriteLine(names[i]);
            }

            // 使用第二个索引器获取数组元素的值
            Console.WriteLine(names["Nuha"]);

            Console.ReadKey();
        }
    }
}

当上面的代码被编译和执行时,它会产生下列结果:

Zara
Riz
Nuha
Asif
Davinder
Sunil
Rubic
N. A.
N. A.
N. A.
2


3、多参数索引器

索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型,可以根据需要返回不同类型的值。以下是一个示例,展示了使用两个参数(整数和字符串)的索引器,并返回整数类型和字符串类型的值:

using System;

namespace MultiIndexerApplication
{
    class MultiIndexer
    {
        // 定义两个数组,一个用于存储整数,一个用于存储字符串
        private int[] intArray = new int[10];
        private string[] stringArray = new string[10];

        // 定义带有两个参数的索引器,一个整数类型的索引,一个字符串类型的索引
        public int this[int index, string type]
        {
            // 索引器的 get 方法,根据类型返回对应的值
            get
            {
                if (type == "int")
                {
                    return intArray[index];  // 返回整数
                }
                else if (type == "string")
                {
                    return int.Parse(stringArray[index]); // 字符串转换为整数后返回
                }
                else
                {
                    return 0;
                }
            }
            // 索引器的 set 方法,根据类型设置对应的值
            set
            {
                if (type == "int")
                {
                    intArray[index] = value; // 给索引结果赋值整数
                }
                else if (type == "string")
                {
                    stringArray[index] = value.ToString(); // 将整数转换成字符串,然后赋值给索引结果
                }
            }
        }

        static void Main(string[] args)
        {
            // 创建 MultiIndexer 类的实例
            MultiIndexer multiIndexer = new MultiIndexer();

            // 设置数组元素的值
            multiIndexer[0, "int"] = 1;
            multiIndexer[0, "string"] = "2";

            // 使用索引器获取数组元素的值,并输出
            Console.WriteLine(multiIndexer[0, "int"]);    // 输出 1
            Console.WriteLine(multiIndexer[0, "string"]); // 输出 2
        }
    }
}

上面的代码中,索引器的两个参数 int indexstring type 分别用来指定下标位置数组类型。当然我们也可以使用多参数来实现多维数组的索引,这里不再举例。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值