C#核心(6)成员属性

前言

我们先前介绍了类中的构造函数,并且了解了一下析构以及垃圾回收,简单对c#底层的内存处理机制做了一定了解,现在我们要介绍一个新的东西,叫成员属性。

成员属性和成员变量有什么区别呢?

成员属性(Property)和成员变量(Field)是 C# 中两种不同的结构,用于封装与对象相关的数据。它们各自有不同的目的和使用方式。

成员变量

定义:成员变量是类或结构体内部的字段,用于直接存储数据。成员变量通常是私有的(使用`private`修饰符),从而遵循封装原则。

访问性:成员变量通常不直接对外部代码公开,建议通过属性进行访问。直接访问成员变量可能导致数据不一致或有效性问题。

cs 复制代码
    public class Person
    {
        private string name; // 成员变量
    }

成员属性

定义:成员属性是对成员变量的封装,它提供了一种通过`get`和`set`访问器来访问和操作成员变量的方法。使用属性可以添加逻辑验证、数据转换等功能。

访问性:成员属性可以是公共的,使外部代码能够安全地访问或修改对象的数据。属性可以实现只读(只有`get`)或只写(只有`set`)的访问。

cs 复制代码
    public class Person
    {
        private string name; // 成员变量

        public string Name // 成员属性
        {
            get { return name; }
            set
            {
                if (string.IsNullOrWhiteSpace(value))
                {
                    throw new ArgumentException("Name cannot be empty.");
                }
                name = value;
            }
        }
    }

这样解释起来你可能觉得还是有一点抽象,那这么说吧,它属于更为详细的变量,在使用它的时候我们可以默认使用一些操作。

具体是什么情况,我们在接下来的文章进行更详细的讲解。

基本概念

  1. 是用来保护成员变量的
  2. 为成员属性的获取和逻辑赋值添加而存在
  3. 解决3p(public,private,protected)的局限性(比如有些private变量我们在传入的时候其实也是希望对其进行一定的修改的)

你看到这个依旧会觉得很抽象,那我们就来看基础语法进行更详细地讲解。

基本语法

cs 复制代码
访问修饰符 属性类型 属性名(一般用帕斯卡命名法,因为本质上是变量)
{
    get{}
    set{}
}

//例子
public string Name
{
    get
    {
        //可以在返回之前加一些逻辑规则
        return name;
    }
    set
    {
        //可以在设置之前加一些逻辑规则
        //value 关键字,用来表示外部传入进来的值
        name=value;
    }
}

看起来还是很抽象对吧,name是什么?Name又是什么?这样说吧,Name是对name的赋值进行控制操作。

我们来看个例子吧

例子

cs 复制代码
public class Person  
{  
    private string name;  

    public string Name  
    {  
        get  
        {  
            return name ?? "Default Name"; // 如果name是null,则返回默认值  
        }  
        set  
        {  
            if (string.IsNullOrWhiteSpace(value)) //读英文或者查文档 
            {  
                throw new ArgumentException("不能为空或者无效");  
            }  
            name = value;  
        }  
    }  
}
//使用
class Program  
{  
    static void Main(string[] args)  
    {  
        // 创建Person类的实例  
        Person person = new Person();  
        
        // 设置Name属性  
        try  
        {  
            person.Name = "Alice"; // 赋值给Name属性  
            Console.WriteLine(person.Name); // 获取并输出Name属性的值  
        }  
        catch (ArgumentException ex)  
        {  
            Console.WriteLine(ex.Message); // 输出异常信息  
        }  

        // 设置一个无效的Name  
        try  
        {  
            person.Name = ""; // 试图设置为空字符串  
        }  
        catch (ArgumentException ex)  
        {  
            Console.WriteLine(ex.Message); // 输出异常信息  
        }  
        
        // 获取Name属性(如果设置成功)  
        Console.WriteLine(person.Name); // 输出: Alice  

        // 如果name为null,返回默认值  
        Person person2 = new Person();  
        Console.WriteLine(person2.Name); // 输出: Default Name  
    }  
}

通过这个例子,我们也能很好get到成员属性存在的意义。

get和set

我们来解释一下get和set吧。

|----|--------------------|
| 赋值 | set(可以用于游戏相关数据的加密) |
| 调用 | get(可以用于游戏相关数据的解密) |

表格中的解释其实也是给了我们一些在游戏种使用二者的原因,比如反外挂,有些数值我们就可以通过一些公式加密解密,让一些外挂不能暴力修改数值达到目的

当然,其实你也可以在这里面做一些基本的逻辑操作,这里博主就不多加解释了。

get,set前可以加访问修饰符

  • 默认不加,会使用属性申明时的访问权限,也就是成员属性的访问权限决定了get和set的默认权限
  • 加的访问修饰符要低于成员属性的访问修饰符
  • 不能让get和set的访问修饰符都低于属性权限

解释:如果get和set方法的访问修饰符都低于属性权限,那么外部代码就可以直接访问属性的值,绕过了set方法的校验和控制。这样就容易破坏属性的封装性和安全性,导致类的数据不可靠。

get和set可以只有一个

也就是只得不改(比如有些默认的东西,玩家的跳跃高度,我们就只希望在外面得到使用,但是我们并不需要去动他),或者只改不得(比如游戏的版本号,我们只用在用户安装的一瞬间修改就行了,后续就不用继续修改了)。

tips:只有一个时,没必要在前面加访问修饰符,一般情况只会出现只有get的情况,也就是只得不改。

自动属性

作用:外部能得不能改的时候,而且又没什么特殊处理,那么直接使用自动属性。

例子:

以下是博主用c#实现的一个角色示例,里面包括了以上的所有知识点,包括访问修饰符的权限,以及自动属性,大家可以试着敲一敲,来更好地理解成员属性的作用,博主尽量写了注释,方便你更好理解成员属性。

cs 复制代码
public class Character  
{  
    // 私有字段  
    private string characterId; // 角色的唯一标识符  
    private string name;         // 角色名字  
    private int level;          // 角色等级  
    private int health;         // 角色的健康值  

    // 只读属性:角色唯一标识符,外部无法修改  
    public string CharacterId  
    {  
        get { return characterId; }  
    }  

    // 公共属性:名字,允许外部读取和修改  
    public string Name  
    {  
        get { return name; }  
        set { name = value; } // 外部可以修改名字  
    }  

    // 公共属性:等级,允许外部读取和修改,包含逻辑来限制合法值  
    public int Level  
    {  
        get { return level; }  
        set  
        {  
            // 应用简单的限制,等级必须在1到100之间  
            if (value < 1)  
            {  
                level = 1;  
            }  
            else if (value > 100)  
            {  
                level = 100;  
            }  
            else  
            {  
                level = value;  
            }  
        }  
    }  

    // 只读属性:健康值,只能由内部逻辑修改  
    public int Health  
    {  
        get { return health; }  
        private set { health = value; } // 外部无法修改健康值  
    }  

    // 自动属性:魔力值  
    public int Magic { get; set; } // 默认访问修饰符为 public  

    // 构造函数,初始化角色属性  
    public Character(string characterId, string name, int level, int health, int magic)  
    {  
        this.characterId = characterId;  
        this.name = name;  
        this.Level = level; // 使用属性,由于有验证逻辑  
        this.Health = health; // 初始化健康值  
        this.Magic = magic; // 初始化魔力值  
    }  

    // 方法:角色受到伤害  
    public void TakeDamage(int damage)  
    {  
        Health -= damage; // 减少健康值  
        if (Health < 0)  
        {  
            Health = 0; // 确保健康值不低于0  
        }  
    }  
}  

class Program  
{  
    static void Main(string[] args)  
    {  
        // 创建 Character 对象  
        Character character = new Character("C001", "Knight", 1, 100, 50);  

        // 打印角色信息  
        Console.WriteLine($"Character ID: {character.CharacterId}");  
        Console.WriteLine($"Name: {character.Name}, Level: {character.Level}, Health: {character.Health}, Magic: {character.Magic}");  

        // 升级角色  
        character.Level = 10;  
        Console.WriteLine($"After leveling up: {character.Name} is now level {character.Level}");  

        // 角色受到伤害  
        character.TakeDamage(30);  
        Console.WriteLine($"{character.Name} took damage! Current Health: {character.Health}");  

        // 尝试修改 CharacterId 会导致编译错误  
        // character.CharacterId = "C002"; // Uncommenting this line will cause a compile-time error  
        
        // 修改角色的名字  
        character.Name = "Heroic Knight";  
        Console.WriteLine($"Updated Name: {character.Name}");  

        // 修改魔力值  
        character.Magic = 75;  
        Console.WriteLine($"Updated Magic: {character.Magic}");  
    }  
}

总结

核心的知识都普遍比较干,大家一定要耐心敲代码,然后耐心学习,切记浮躁,你现在确实可以写一些简单游戏了,但是你如果想写出令自己满意的游戏,令玩家满意的游戏,路还很长,这些基础相当重要,也是你未来的基石。

请期待我的下一篇文章,请务必将我给出的例子自己实现后消化理解,这些东西在日后的使用也是格外频繁的。

我来自佑梦游戏开发,欢迎你的关注和订阅!

相关推荐
F-2H1 分钟前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
bryant_meng1 小时前
【python】OpenCV—Image Moments
开发语言·python·opencv·moments·图片矩
若亦_Royi2 小时前
C++ 的大括号的用法合集
开发语言·c++
资源补给站2 小时前
大恒相机开发(2)—Python软触发调用采集图像
开发语言·python·数码相机
m0_748247553 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
6.943 小时前
Scala学习记录 递归调用 练习
开发语言·学习·scala
FF在路上3 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
码农君莫笑4 小时前
使用blazor开发信息管理系统的应用场景
数据库·信息可视化·c#·.net·visual studio
众拾达人4 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.4 小时前
Mybatis-Plus
java·开发语言