C#,结构成员是引用,会发生什么

本文围绕C#中结构(struct)和类(class)展开,探讨结构里有引用类型成员时的情况。通过OneLine结构示例及测试代码,发现结构赋值等同于类的浅表复制,而非完全复制。作者还给出Copy方法解决赋值异常问题,并附上OneLine结构代码。

大家知道.结构(struct)是值类型,类(class)是引用类型,现在我们考虑一个问题,如果结构里面有引用类型的成员,那会产生什么样的情况?
现在我们有一个结构OneLine,先介绍一下它的特性,它对外基本可以看作是一行字符串,但同时它又是由几部分构成的,我举个例:"ABCD",在某些应用中,我可能需要知道这个字符串是由哪几个元素构成的,是A/B/C/D还是ABC/D,而且考虑到这一行是可以添加的,所以我用了一个ArrayList来放入这些元素,以后调用的时候,就知道了,噢,是ABC/D,而不是A/B/C/D,从而作出相应的处理;
现在,我们来看我的测试代码(附调试器图片):
OneLine Line_1=new OneLine("test");
OneLine Line_2=Line_1;
OneLine Line_3=Line_1.Copy();
Line_1.Add("new_line");
return;
如果结构中的引用类型在结构被赋值给另一个结构的时候是复制一份副本,那么Line_2和Line_3应该是一样的.
我在写一个程序时就这么想当然了..结果出现了令人很抓狂的异常,见图,比如你在下面的代码里使用Line_2的时候,你如果认为它里面只有"test"这一个元素,错误就在所难免了.
找出了这个问题,我就写了一个Copy方法,来返回这个结构的复制,问题就再也没有出现,可以从图上看到,Line_3按照我们的预期在运行.

最后,简单表述一下今天这段异常让我学习到的..
结构的赋值,应该是等同于类的浅表复制(成员是值就复制值,成员是引用就复制引用),而不是完全复制..
Appledotnet@hotmail.com  2004/07/08   附OneLine结构代码见后

 

public struct OneLine
{
    int Length;
    string Line;
    ArrayList AL;
    public OneLine(string Line)//初始化
    {
        this.Line=Line;
        this.Length=Line.Length;
        this.AL=new ArrayList();
        this.AL.Add(Line);
    }
    public bool Add(string newLine)//增加一行
    {
        foreach(object obj in AL)
        if(((string)obj)==newLine)return false;
        this.AL.Add(newLine);
        this.Line=this.Line+newLine;
        this.Length=this.Line.Length;
        return true;
    }
    override public string ToString()
    {
        return Line;
    }
    public OneLine Copy()//返回一个复制的副本
    {
        OneLine OL=new OneLine(this.AL[0].ToString());
        for(int i=1;i< AL.Count;i++)
            OL.Add(AL[i].ToString());
        return OL;
    }
}

<think>我们正在讨论C#中所有引用类型的基类。根据C#语言规范,所有引用类型都直接或间接继承自一个特定的基类。以下是关键点: 1. **引用类型的基类**:在C#中,所有引用类型(包括类、接口、数组、委托等)都继承自`System.Object`类(别名`object`)[^1][^3][^4]。这是C#类型系统的根基。 2. **值类型的基类**:虽然问题聚焦引用类型,但作为对比,值类型(如`int`、`struct`)隐式继承自`System.ValueType`,而`ValueType`本身又继承自`System.Object`[^4]。因此,值类型也间接属于`object`的子类。 3. **object类的关键作用**: - 提供基础方法:`ToString()`、`Equals()`、`GetHashCode()`和`GetType()`,这些方法可被任何类型覆写[^3]。 - 引用类型变量可声明为`object`类型,以引用任何对象(多态性): ```csharp object obj = "字符串"; // 合法,字符串是引用类型 obj = new int[10]; // 合法,数组是引用类型 ``` 4. **特殊引用类型示例**: - **字符串(string)**:`System.String`,继承自`object`[^3][^4]。 - **数组**:如`int[]`,继承自`System.Array`,而`Array`继承自`object`[^3]。 - **自定义类**:默认继承`object`,除非显式指定其他基类[^1][^2]。 5. **引用转换**:派生类引用可隐式转换为基类(如`object`)引用,称为向上转型(upcasting)[^2]: ```csharp string s = "text"; object obj = s; // 合法,因为stringobject的子类 ``` **总结**:C#中所有引用类型的基类是`System.Object`(简写`object`)。它定义了所有类型的基础行为,并支持多态性。值类型通过`System.ValueType`间接继承`object`,因此C#的整个类型系统统一于`object`[^1][^3][^4]。 §§相关问题§§ 1. `System.Object`类中定义了哪些核心方法?如何在自定义类中覆写这些方法? 2. 为什么值类型需要装箱(boxing)才能转换为`object`引用? 3. 接口(interface)是否继承自`System.Object`? 4. 如何通过`object`引用实现运行时的多态性(polymorphism)? [^1]: 引用[1]:一般来说,值类型(不支持多态)适合存储供 C#应用程序操作的数据,而引用类型(支持多态)应该用于定义应用程序的行为。通常我们创建的引用类型总是多于值类型。如果满足下面情况,那么我们就应该创建为值类型。 [^2]: 引用[2]:C# 类和继承(使用基类的引用)。如果有一个派生类对象的引用,就可以获取该对象基类部分的引用(使用类型转换运算符把该引用转换为基类类型)。 [^3]: 引用[3]:1、类包含数据成员、函数成员、嵌套类型的数据结构;其中数据成员有常量变量、域和事件,函数成员包括方法、属性、索引指示器、运算符、构造函数和析构函数,类和结构同样包含成员,他们两个的区别在于类是引用类型,结构是值类型;2、特殊类object,该类为所有其他类的基类,C#的任何类型的值都直接或间接的继承于该类,所以对于一个object类可以直接或间接的赋予任何类型的值,名称空间为System.object。 [^4]: 引用[4]:C#值类型和引用类型。引用类型:基类为Objcet 值类型:均隐式派生自System.ValueType。</think>### C# 中所有引用类型的基类 在 C# 类型系统中,**所有引用类型的基类都是 `System.Object`**(简写为 `object`)。这是 C# 语言设计的核心基础,具有以下关键特性: 1. **统一继承根源** - 所有引用类型(类、接口、数组、委托等)都直接或间接继承自 `System.Object`[^3][^4] - 值类型通过 `System.ValueType` 间接继承自 `System.Object`,但值类型不是引用类型[^4] 2. **核心功能** `object` 类提供所有类型的基础方法: ```csharp public class Object { public virtual bool Equals(object? obj); public virtual int GetHashCode(); public virtual string? ToString(); public Type GetType(); // 获取运行时类型信息 protected object MemberwiseClone(); // 创建浅拷贝 } ``` 3. **多态性支持** 引用类型可通过基类引用实现多态行为[^1][^2]: ```csharp object obj = new List<string>(); // 合法:List<T> 是引用类型 obj = "Hello World"; // 合法:string引用类型 obj = new int[10]; // 合法:数组是引用类型 ``` 4. **特殊引用类型示例** | 引用类型 | 继承关系 | |----------------|----------------------------------| | 类 (class) | `MyClass : object` (默认隐式继承) | | 字符串 (string)| `String : object` | | 数组 (Array) | `int[] : Array : object` | | 委托 (delegate)| `Action : Delegate : object` | | 接口 (interface)| 不继承 object,但实现接口的类必须继承 object | 5. **类型验证** 可通过 `is` 或 `GetType()` 验证继承关系: ```csharp string s = "test"; Console.WriteLine(s is object); // 输出 True Console.WriteLine(s.GetType().BaseType); // 输出 System.Object ``` ### 关键区别:值类型 vs 引用类型 | 特性 | 值类型 | 引用类型 | |--------------|------------------------|------------------------| | **基类** | `System.ValueType` | `System.Object` | | **存储位置** | 栈内存 | 堆内存 | | **赋值行为** | 复制整个值 | 复制引用(地址) | | **空值** | 不可为 `null` (通常) | 可为 `null` | | **示例** | `int`, `struct`, `enum`| `class`, `string`, 数组 | > ⚠️ 注意:虽然值类型通过 `System.ValueType` 间接继承 `object`,但**值类型本身不是引用类型**。当值类型被装箱(boxing)时会转换为引用类型[^4]。 [^1]: 引用[1]: 值类型适合存储数据,引用类型用于定义行为,且引用类型总是多于值类型 [^2]: 引用[2]: 派生类对象可转换为基类引用,通过类型转换运算符实现 [^3]: 引用[3]: object 类为所有其他类的基类,C# 任何类型的值都直接或间接继承于该类 [^4]: 引用[4]: 引用类型基类为 Object,值类型隐式派生自 System.ValueType
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值