CodeGym /课程 /C# SELF /声明类和对象时常见的错误

声明类和对象时常见的错误

C# SELF
第 25 级, 课程 0
可用

1. 名字重复问题:字段、属性、参数

新手经常遇到属性名、构造函数参数名和私有字段名一样的情况。这种情况下编译器不会报错,但结果可能不是你想要的。

例子

public class Student
{
    private string name;

    public Student(string name)
    {
        name = name; // “看起来没毛病...”
    }

    public void PrintName()
    {
        Console.WriteLine(name);
    }
}

会发生什么?PrintName()方法啥都不会打印:私有字段name还是null!因为在构造函数里name = name;其实只操作了参数,不是类的字段。

怎么避免

用this关键字来访问类的字段/属性:

public Student(string name)
{
    this.name = name; // 现在没问题了!
}

小贴士:Rider和其他IDE会帮你发现这种问题——别无视它们的提示!

2. 没有默认构造函数:什么时候需要?

如果你声明了至少一个自己的构造函数,编译器就不会再自动生成“空”的默认构造函数。这经常导致错误,尤其是你想无参数创建对象时。

例子

public class Book
{
    public string Title { get; set; }

    // 明确声明的构造函数
    public Book(string title)
    {
        Title = title;
    }
}

var book = new Book(); // 编译器:“哔哔!没有无参数构造函数!”

如果你想既能无参数又能有参数创建实例,记得手动加上需要的构造函数:

public Book() { }

public Book(string title)
{
    Title = title;
}

3. 访问修饰符:私生活的公开人物

新手常犯的错——没考虑访问修饰符。默认情况下,类的字段和方法是private,结构体成员也是private。不写或者忘了写,可能导致你在类外用不了需要的属性或方法。

例子

public class Animal
{
    string name; // 默认private

    public void PrintName()
    {
        Console.WriteLine(name);
    }
}

var animal = new Animal();
animal.name = "巴尔西克"; // 编译错误!

编译器不会让你访问私有成员。最好明确写上修饰符:

public string Name;

或者

private string name;

更推荐用属性来访问:

public string Name { get; set; }

4. 忘记初始化字段:null陷阱

很常见的坑——类的字段没初始化。尤其是字段会在显式初始化前被方法用到时。

例子

public class Cat
{
    public string Name;

    public void SayHello()
    {
        Console.WriteLine($"喵!名字长度{Name.Length}个字!"); // 可能NullReferenceException
    }
}

var cat = new Cat();
cat.SayHello(); // 悲剧了!

变量Name等于null,访问Length就会抛NullReferenceException。创建对象后一定要注意它的状态!

建议:用构造函数强制设置重要字段,属性可以标记为必填(新C#用required)。

5. 自动属性无限递归:复制粘贴的锅

有时候手写属性时会写错——比如getter或setter里又访问自己(不是私有字段),结果死循环。

例子

public class Dog
{
    public int Age
    {
        get { return Age; } // 哎呀!
        set { Age = value; } // 哎呀呀!
    }
}

这种代码每次访问Age都会自己调自己...无限递归(直到栈溢出——递归自虐)。

正确写法

用单独的私有字段:

private int age;

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

或者直接用自动属性:

public int Age { get; set; }

6. 名字不统一

编程时大小写和命名很重要。C#是区分大小写的(Case-Sensitive),Name、name、NAME是三个不同的标识符。

还有,如果你改了字段或属性名,记得所有地方都要一起改!Rider能帮你重构,但拼写相近时也可能出错。

7. 用错修饰符(static/instance)

新手有时会把实例方法和静态方法搞混。问题出在你想用C#语法不允许的方式访问字段/方法时。

例子

public class MathHelper
{
    public static int Add(int a, int b) => a + b;
    public int Multiply(int a, int b) => a * b;
}

var sum = MathHelper.Add(2, 3); // OK
var mul = MathHelper.Multiply(2, 3); // 错误! 

Multiply不是静态方法,不能用类名直接调,得先new对象:

var helper = new MathHelper();
var mul = helper.Multiply(2, 3); // 这样才对

8. 继承构造函数时的错误

如果你的类继承自别的类,别忘了基类可能要求构造函数传参数。

例子

public class Animal
{
    public string Name;

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

public class Dog : Animal
{
    public Dog() { } // 错误:基类要求name!
}

编译器不会让你这样写。要显式调用基类构造函数:

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

9. 忘了实现接口的所有成员

如果你的类实现了接口,但没实现所有方法——编译就会报错:“类没有完全实现接口”。

例子

public interface IWalker
{
    void Walk();
    void Run();
}

public class Turtle : IWalker
{
    public void Walk()
    {
        Console.WriteLine("慢慢来,稳稳的...");
    }
    // 哎呀!Run忘了!
}

IDE会报错,Turtle编译不过,直到你把接口所有方法都实现。

10. 用了没初始化的对象

在C#里对象要用new显式创建。如果你只是声明了引用变量但没赋对象——它就是null。访问字段/方法时就会遇到著名的NullReferenceException。

例子

Student student;
student.PrintName(); // 惊喜!

一定要初始化对象:

Student student = new Student("瓦西丽萨");
student.PrintName();

11. 错误:public字段代替属性

老“反模式”:把类的所有字段都声明成public。为啥不好?字段没保护,无法控制修改,封装性全无!

例子

public class Car
{
    public int speed; // 别这样!
}

最好用属性:

public class Car
{
    public int Speed { get; set; }
}

属性可以加校验、逻辑、限制:

private int speed;
public int Speed
{
    get { return speed; }
    set
    {
        if (value < 0) speed = 0;
        else speed = value;
    }
}

12. 隐式创建“魔法数字”或字符串

如果你在代码里看到42、"伊万"、"SomeFile.txt"等“魔法”值直接写在方法体里——这是潜在的坑。最好用常量或字段。

例子

public class ConfigManager
{
    public void Save()
    {
        File.WriteAllText("config.txt", "some data"); // 魔法字符串!
    }
}

更好的写法:

public class ConfigManager
{
    private const string ConfigFileName = "config.txt";
    public void Save()
    {
        File.WriteAllText(ConfigFileName, "some data");
    }
}

13. 复制错误:变量引用同一个对象

在C#里,变量-对象其实是引用。把一个变量赋给另一个,只是复制了引用,不是对象本身。这经常导致“突然”变化。

例子

Student s1 = new Student("瓦夏");
Student s2 = s1;
s1.Name = "科利亚";
Console.WriteLine(s2.Name); // 会输出“科利亚”!

为啥?因为s1和s2其实指向同一个对象!

14. 错误时没有反馈

很糟糕的是,类允许赋不合理的值。比如负年龄。

例子

public class Human
{
    public int Age { get; set; }
}

var h = new Human();
h.Age = -50; // 没问题...

类的逻辑应该防止无意义的值:

private int age;
public int Age
{
    get { return age; }
    set
    {
        if (value < 0)
            throw new ArgumentException("年龄不能为负。");
        age = value;
    }
}

15. 作用域混乱(scope)

有时候变量在不同作用域里重名——方法里和类字段一样。

例子

public class MyClass
{
    int value = 10;

    public void Foo()
    {
        int value = 99;
        Console.WriteLine(value); // 会输出99,不是10!
    }
}

要明确访问类字段,用this.value。

16. 重写方法时忘了override关键字

你想重写基类的虚方法时,别忘了override!

例子

public class Animal
{
    public virtual void Speak() => Console.WriteLine("动物说话");
}

public class Cat : Animal
{
    public void Speak() => Console.WriteLine("喵!"); // 不是override!
}

这里基类方法没被重写:新方法只是隐藏了老的(shadowing),用基类引用时可能出意外。

正确写法:

public override void Speak() => Console.WriteLine("喵!");

17. 在构造函数里调用虚方法

这不算编译错误,但经常导致难查的bug。如果在基类构造函数里调用虚方法,而这个方法在子类被重写——调用时子类字段可能还没初始化。

例子

public class Animal
{
    public Animal()
    {
        Speak();
    }

    public virtual void Speak()
    {
        Console.WriteLine("动物...");
    }
}

public class Cat : Animal
{
    public string Name { get; set; } = "巴尔西克";

    public override void Speak()
    {
        Console.WriteLine($"喵!我是{Name}");
    }
}

Cat cat = new Cat();
// 调用Speak()时Cat构造函数还没执行完,Name还是null!

18. 只用构造函数描述对象(没有属性)

如果类只有构造函数,创建后对象就不能改——适合不可变对象,但大多数时候最好让类的使用者能改属性(至少部分)。

19. 忽略标准命名规范(naming conventions)

类名首字母大写,方法也是,字段小写加下划线(或camelCase),常量全大写。统一风格不仅让代码更好读,还能减少低级错误!

20. 没用到的字段和方法

如果你声明了字段但没用过——那就是“死代码”。IDE会高亮这些。死角越少,项目越好维护!

2
任务
C# SELF, 第 25 级, 课程 0
已锁定
修正同名变量错误并使用关键字 `this`
修正同名变量错误并使用关键字 `this`
2
任务
C# SELF, 第 25 级, 课程 0
已锁定
添加默认构造函数
添加默认构造函数
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION