想象一下,你正在建造一座乐高城堡。你不会在墙壁上使用与窗户相同的部件,对吧?在 C# 中,类就像你的乐高积木,不同类型的类适用于应用程序中的不同工作。这篇文章将讨论 C# 中的各种类类型,并帮助你为你的项目选择正确的类类型!
引用类型
这些类型存储对实际数据位置的引用(内存地址)。将引用类型变量分配给另一个变量会创建内存地址的副本,而不是数据本身。这对于经常修改的复杂对象很有用。
类:最常见的用户定义蓝图
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public void Welcome()
{
Console.WriteLine($"Hello, my name is {Name}!");
}
}
public class Program {
public static void Main()
{
User user1 = new User { Name = "Niraj", Age = 30 };
User user2 = user1; // Assigning a reference type variable creates a copy of the reference
user1.Welcome(); // Output: Hello, my name is Niraj!
user2.Name = "Alice"; // Modifying user2 also affects user1 due to shared reference
user1.Welcome(); // Now outputs: Hello, my name is Alice!
}
}
接口:类要遵循的契约,促进代码的可重用性
public interface IProduct
{
string GetName();
double GetPrice();
}
public class Product : IProduct // Concrete class implementing the interface
{
public string Name { get; set; }
public double Price { get; set; }
public string GetName()
{
return Name;
}
public double GetPrice()
{
return Price;
}
}
public static class Program
{
public static void Main()
{
IProduct product = new Product { Name = "Book", Price = 24.95 };
Console.WriteLine($"Product name: {product.GetName()}"); // Output: Product name: Book
Console.WriteLine($"Product price: {product.GetPrice():C2}"); // Output: Product price: $24.95
}
}
抽象类:继承的基类,将在后面部分解释。
Delegate :对具有特定签名的方法的引用
public class DelegateSample
{
public delegate int StringToIntDelegate(string value); // Delegate signature
public static int ParseInt1(string value)
{
return int.Parse(value);
}
public static int ParseInt2(string value)
{
return int.Parse(value) + 10;
}
}
using static DelegateSample;
public static class Program
{
public static void Main()
{
StringToIntDelegate converter1 = ParseInt1; // Assigning a method to the delegate
StringToIntDelegate converter2 = ParseInt2; // Assigning a method to the delegate
int number1 = converter1("10"); // Calling the delegate (which calls ParseInt1)
int number2 = converter2("10"); // Calling the delegate (which calls ParseInt2)
Console.WriteLine($"Converted number: {number1}"); // Output: Converted number: 10
Console.WriteLine($"Converted number: {number2}"); // Output: Converted and added 10 : 20
}
}
我们定义了一个委托类型,该类型指定了一个接受字符串并返回一个整数的方法。该方法是执行字符串到整数转换的具体方法。该方法是执行字符串到整数转换并加 10 的另一种具体方法。
然后,我们将该方法分配给委托变量。现在,包含对 的引用。和 也是如此。
值类型
相反,值类型将数据的实际值直接存储在变量本身中。分配值类型变量将创建全新的数据副本。这对于性能至关重要的简单数据结构非常有用。
结构体:类似于类,但直接在变量中存储数据
using System;
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Translate(int dx, int dy)
{
X += dx;
Y += dy;
}
}
public static class Program
{
public static void Main()
{
Point point1 = new Point { X = 5, Y = 10 };
Point point2 = point1; // Assigning a value type variable creates a completely new copy
point1.Translate(2, 3); // Modifying point1 doesn't affect point2
Console.WriteLine($"point1: ({point1.X}, {point1.Y})"); // Output: (7, 13)
Console.WriteLine($"point2: ({point2.X}, {point2.Y})"); // Output: (5, 10) (Unaffected)
}
}
枚举:一组命名的整数常量
public enum DayOfWeek
{
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
public static class Program
{
public static void Main()
{
DayOfWeek today = DayOfWeek.Thursday; // Assigning an enum value
Console.WriteLine($"Today is {today}"); // Output: Today is Thursday
// Accessing enum values by index
int todayIndex = (int)today; // Enum can be implicitly cast to its underlying type (int)
Console.WriteLine($"Today's index is: {todayIndex}"); // Output: Today's index is: 3
// Using switch statement with enums
switch (today)
{
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
Console.WriteLine("It's the weekend!");
break;
default:
Console.WriteLine("Back to work!");
break;
}
}
}
在这里,我们定义了一个以命名常量命名的枚举,用于一周中的每一天。
标准班
.NET Framework 提供了大量预定义的类,如 、 和 。这些提供了您可以轻松使用的常用功能。
public static class Program
{
public static void Main()
{
string message = "This is a string!";
int length = message.Length; // Get string length
Console.WriteLine(length); // Output: 17
}
}
自定义类
这些类封装了特定于问题域的数据(属性)和功能(方法)(例如,、、)。
public class Order
{
public int OrderId { get; set; }
public List<Product> Items { get; set; } // Using another standard class (List)
public double CalculateTotal()
{
double total = 0;
foreach (Product item in Items)
{
total += item.Price;
}
return total;
}
}
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
public static class Program
{
public static void Main()
{
Order order = new Order();
order.OrderId = 100;
order.Items = new List<Product>();
order.Items.Add(new Product { Name = "Shirt", Price = 19.99 });
order.Items.Add(new Product { Name = "Hat", Price = 14.50 });
double total = order.CalculateTotal();
Console.WriteLine($"Order total: {total:C2}"); // Output: $34.49
}
}
此自定义类管理订单详细信息,并使用自定义方法计算总价。该类表示订单中的产品。
抽象类(继承的基类)
抽象类充当其他类要继承的蓝图。它可以定义常见的属性、方法,甚至是抽象方法(无需实现)。然后,子类(也称为具体类)必须实现这些抽象方法来提供特定的功能。这对于定义基本行为和对派生类强制执行某些规则非常有用。
public abstract class Shape
{
public abstract void Draw(); // Enforces subclasses to implement drawing behavior
}
public static class Program
{
public static void Main()
{
// You cannot directly create an instance of an abstract class
Shape shape = new Shape(); // Compiler error!
}
}
抽象类定义了形状的蓝图,规定了子类(如下图所示)必须实现的方法。
混凝土类(全功能类)
具体类是一个功能齐全的类,可以实例化并直接使用。它实现了所有必需的功能,并且不包含任何抽象方法。您创建的大多数自定义类都将是具体类。
public abstract class Shape
{
public abstract void Draw(); // Enforces subclasses to implement drawing behavior
}
public class Circle : Shape
{
public double Radius { get; set; }
public override void Draw() // Implements abstract Draw method
{
Console.WriteLine("Drawing a circle with radius: {0}", Radius);
}
}
public static class Program
{
public static void Main()
{
Circle circle = new Circle { Radius = 5.0 };
circle.Draw(); // Output: Drawing a circle with radius: 5
}
}
该类继承自该方法,并为该方法提供具体的实现,特别是对于圆。
密封类(防止继承)
密封类可防止其他类从它继承。这对于表示最终完整实现的类非常有用,不应进一步扩展。
public sealed class MathUtil // Sealed class to prevent inheritance
{
public static int Add(int a, int b)
{
return a + b;
}
}
public class ScienceUtil : MathUtil// This class cannot inherit from MathUtil due to being sealed
{
// Error: 'NotAMathUtil' cannot inherit from sealed type 'MathUtil'
}
在这里,我们定义了一个名为 的密封类。这样可以防止任何其他类从它继承。然后,我们尝试创建一个继承自 的类。但是,代码将导致编译器错误,因为代码是密封的。
静态类(实用函数和常量)
无法实例化,仅包含静态成员(静态方法和属性)。静态类通常用于不需要创建对象的实用工具函数或常量。(例如,MathStringExtensions)
public static class MathUtil
{
public static int Add(int a, int b)
{
return a + b;
}
}
public static class Program
{
public static void Main()
{
int sum = MathUtil.Add(5, 3);
Console.WriteLine($"Sum: {sum}"); // Output: Sum: 8
}
}
静态类提供了像加法 () 这样的实用函数,可以直接使用,而无需创建此类的对象。
嵌套类(在另一个类中定义)
在另一个类中定义,创建层次结构关系。这对于帮助程序类或与外部类紧密耦合的类非常有用。
public class OrderProcessor
{
public class OrderDetails
{
public int ItemCount { get; set; }
}
public int ProcessOrder(Order order)
{
OrderDetails details = new OrderDetails(); // Create nested class instance
if (order.Items != null) // Check if Items is null
{
details.ItemCount = order.Items.Count;
return details.ItemCount;
}
else
{
return 0; // Set ItemCount to 0 if Items is null
}
}
}
public class Order
{
public int OrderId { get; set; }
public List<Product> Items { get; set; } = new List\<Product>();
public double CalculateTotal()
{
double total = 0;
foreach (Product item in Items)
{
total += item.Price;
}
return total;
}
}
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
public static class Program
{
public static void Main()
{
// Creating sample products
Product product1 = new Product { Name = "Product 1", Price = 10.50 };
Product product2 = new Product { Name = "Product 2", Price = 20.75 };
// Creating sample order with products
Order order1 = new Order();
order1.Items.Add(product1);
order1.Items.Add(product2);
// Processing the order
OrderProcessor processor = new OrderProcessor();
int count1 = processor.ProcessOrder(order1);
// Displaying order details
Console.WriteLine("Order 1 Details:");
Console.WriteLine($"Total Products: {count1}");
Console.WriteLine($"Total Price: ${order1.CalculateTotal()}");
// Creating another sample order
Order order2 = new Order();
order2.Items.Add(product1); // Adding the same product as before
// Processing the second order
int count2 = processor.ProcessOrder(order2);
// Displaying second order details
Console.WriteLine("\\nOrder 2 Details:");
Console.WriteLine($"Total Products: {count2}");
Console.WriteLine($"Total Price: ${order2.CalculateTotal()}");
}
}
嵌套在 中的类有助于管理与外部类的功能紧密耦合的特定订单详细信息。
Record 类(不可变的数据传输对象)
一种特殊类型的类,针对不可变性和值语义进行了优化,可用于数据传输对象 (DTO)。
public record RecordPoint(int X, int Y) // Record class with constructor
{
public override string ToString() => $"({X}, {Y})";
}
public static class Program
{
public static void Main()
{
RecordPoint point1 = new RecordPoint(5, 10);
Console.WriteLine(point1); // Output: (5, 10)
// Record classes are immutable (cannot modify after creation)
// point1.X = 15; // Compiler error!
RecordPoint point2 = point1 with { X = 15 }; // Create a copy with modified X
Console.WriteLine(point2); // Output: (15, 10)
}
}
该类是一个记录类,提供属性和可能的构造函数。默认情况下,记录类是不可变的,这意味着其数据在创建后无法更改。分配另一个变量将创建数据的新副本。
记录结构(不可变值类型)
类似于 record 类,但是一种值类型,为小型数据结构提供更好的性能。
public record struct StructPoint(int X, int Y) // Record struct with constructor
{
public override string ToString() => $"({X}, {Y})";
}
public static class Program
{
public static void Main()
{
StructPoint point1 = new StructPoint(5, 10);
Console.WriteLine(point1); // Output: (5, 10)
// Record structs are immutable (cannot modify after creation)
// point1.X = 15; // Compiler error!
StructPoint point2 = point1 with { X = 15 }; // Create a copy with modified X
Console.WriteLine(point2); // Output: (15, 10)
}
}
该类是一个记录结构体,提供存储在堆栈上的属性和可能的构造函数。当分配或传递时,将创建记录结构的副本。
分部类(拆分为多个代码文件)
一个类可以拆分为多个代码文件,从而促进大型类更好的组织和可维护性。
// LargeClass.cs
public partial class LargeClass
{
public void Method1()
{
Console.WriteLine("Method 1 from LargeClass");
}
}// LargeClass_Part2.cs
public partial class LargeClass
{
public void Method2()
{
Console.WriteLine("Method 2 from LargeClass_Part2");
}
}// Program.cs
public static class Program
{
public static void Main()
{
LargeClass largeClass = new LargeClass();
largeClass.Method1(); // Output: Method 1 from LargeClass
largeClass.Method2(); // Output: Method 2 from LargeClass\_Part2
}
}
该类在两个单独的文件 ( 和 ) 中定义,演示了分部类的概念。
匿名类(一次性定义和实例化)
同时定义和实例化的类,通常用于临时对象或在短时间内需要特定功能时。
public static void RunAnonymousClassExample()
{
var temporaryObject = new { Name = "Temp Niraj", Value = 10 };
Console.WriteLine($"Name: {temporaryObject.Name}, Value: {temporaryObject.Value}");
// Output: Name: Temp Niraj, Value: 10
}
该方法演示如何创建和使用匿名类。匿名类对于生存期较短的对象非常有用,在这些对象中,定义命名类可能有点矫枉过正。
了解这些类类型有助于您在设计 C# 应用程序时做出明智的决策。
以下是一些指导原则:
通过掌握这些类类型和概念,您将能够很好地在 C#/.NET 中创建健壮、可维护且结构良好的面向对象的程序。