string s = "123"; int result; if (int.TryParse(s, out result)) { Console.WriteLine($"Converted value: {result}"); } else { Console.WriteLine("Conversion failed"); }
int a = 10, b = 3; int sum = a + b; // 13 int difference = a - b; // 7 int product = a * b; // 30 int quotient = a / b; // 3 (整数除法) int remainder = a % b; // 1
int x = 5; x += 3; // 等同于 x = x + 3 x -= 2; // 等同于 x = x - 2 string name = null; name ??= "John"; // 如果name为null,赋值为"John"
比较运算符
C#和Java的比较运算符完全相同:
等于 (==)
不等于 (!=)
大于 (>)
小于 (<)
大于等于 (>=)
小于等于 (<=)
示例:
int a = 5, b = 7; bool isEqual = (a == b);// false bool isNotEqual = (a !=b); // true bool isGreater = (a > b);// false bool isLess = (a < b); // true bool isGreaterOrEqual = (a >= b); // false bool isLessOrEqual = (a <= b);// true
逻辑运算符
C#和Java的逻辑运算符也是相同的:
逻辑与 (&&)
逻辑或 (||)
逻辑非 (!)
示例:
bool a = true, b = false; bool andResult = a && b; // false bool orResult = a || b; // true bool notResult = !a; // false
位运算符
C#和Java的位运算符也基本相同:
按位与 (&)
按位或 (|)
按位异或 (^)
按位取反 (~)
左移 (<<)
右移 (>>)
C#特有的位运算符:
无符号右移 (>>>)
示例:
int a = 60;// 二进制: 0011 1100 int b = 13; // 二进制: 0000 1101 int c = a & b; // 12(二进制: 0000 1100) int d = a | b; // 61 (二进制: 0011 1101) int e = a ^ b; // 49 (二进制: 0011 0001) int f = ~a; // -61 (二进制: 1100 0011, 补码表示) int g = a << 2; // 240 (二进制: 1111 0000) int h = a >> 2; // 15 (二进制: 0000 1111)
条件运算符
C#和Java都有三元条件运算符:
int a = 10, b = 20; int max = (a > b) ? a : b; // 20
C#特有的条件运算符:
空合并运算符 (??)
空条件运算符(?.)
示例:
string name = null; string displayName = name ?? "Guest"; // "Guest" class Person { public string Name { get; set; } } Person person = null; int? nameLength = person?.Name?.Length; // null
类型测试运算符
C#提供了一些Java中没有的类型测试运算符:
is 运算符:检查对象是否与特定类型兼容
as 运算符:执行类型转换,如果转换失败,返回null
示例:
object obj = "Hello"; if (obj is string) { Console.WriteLine("obj is a string"); } string str = obj as string; if (str != null) { Console.WriteLine($"The string is: {str}"); }
Lambda 表达式
C#和Java都支持Lambda表达式,但语法略有不同:
C#:
Func square = x => x * x; int result = square(5); // 25
Java:
Function square = x -> x * x; int result = square.apply(5); // 25
空合并运算符(??)
C#特有的空合并运算符可以简化处理可能为null的情况:
string name = null; string displayName = name ?? "Guest"; // "Guest"
在Java中,你可能需要这样写:
String name = null; String displayName = (name != null) ? name : "Guest";
表达式体成员 (Expression-bodied members)
C#允许使用更简洁的语法来定义属性和方法:
public class Circle { public double Radius { get; set; } public double Diameter => Radius * 2; public double CalculateArea() => Math.PI * Radius * Radius; }
这种语法在Java中是不存在的。
字符串插值
C#提供了非常方便的字符串插值语法:
string name = "Alice"; int age = 30; string message = $"My name is {name} and I am {age} years old.";
Java在较新的版本中也引入了类似的功能,但语法不同:
String name = "Alice"; int age = 30; String message = String.format("My name is %s and I am %d years old.", name, age);
int x = 10; if (x > 5) { Console.WriteLine("x is greater than 5"); } else if (x < 5) { Console.WriteLine("x is less than 5"); } else { Console.WriteLine("x is equal to 5"); }
C#特有的特性:
可空类型的使用:
int? x = null; if (x.HasValue) { Console.WriteLine($"x has a value: {x.Value}"); } else { Console.WriteLine("x is null"); }
模式匹配(C# 7.0+):
object obj = "Hello"; if (obj is string s) { Console.WriteLine($"The string is: {s}"); }
switch 语句
C#的switch语句比Java的更加灵活:
int day = 3; switch (day) { case 1: Console.WriteLine("Monday"); break; case 2: Console.WriteLine("Tuesday"); break; case 3: case 4: case 5: Console.WriteLine("Midweek"); break; default: Console.WriteLine("Weekend"); break; }
C#特有的特性:
模式匹配(C# 7.0+):
object obj = 123; switch (obj) { case int i when i > 100: Console.WriteLine($"Large integer: {i}"); break; case string s: Console.WriteLine($"String value: {s}"); break; case null: Console.WriteLine("Null value"); break; default: Console.WriteLine("Unknown type"); break; }
switch 表达式(C# 8.0+):
string GetDayType(int day) => day switch { 1 => "Monday", 2 => "Tuesday", 3 or 4 or 5 => "Midweek", _ => "Weekend" };
循环语句
C#和Java的循环语句非常相似:
for循环:
for (int i = 0; i < 5; i++) { Console.WriteLine(i); }
while 循环:
int i = 0; while (i < 5) { Console.WriteLine(i); i++; }
do-while 循环:
int i = 0; do { Console.WriteLine(i); i++; } while (i < 5);
foreach 循环:
string[] fruits = { "apple", "banana", "cherry" }; foreach (string fruit in fruits) { Console.WriteLine(fruit); }
C#特有的特性:
LINQ与foreach的结合:
List numbers = new List { 1, 2, 3, 4, 5 }; foreach (var num in numbers.Where(n => n % 2 == 0)) { Console.WriteLine(num); }
跳转语句
C#和Java都支持以下跳转语句:
break:跳出当前循环或switch语句
continue:跳过当前循环的剩余部分,开始下一次迭代
return:从方法中返回,并可选择返回一个值
C#特有的跳转语句:
goto:虽然不推荐使用,但C#保留了goto语句
int i = 0; start: if (i < 5) { Console.WriteLine(i); i++; goto start; }
异常处理
C#和Java的异常处理机制非常相似:
try { int result = 10 / 0; } catch (DivideByZeroException ex) { Console.WriteLine($"Division by zero error: {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"An error occurred: {ex.Message}"); } finally { Console.WriteLine("This always executes"); }
public void IncrementValue(int x) { x++; // 不影响原始值 }
引用参数(ref 关键字):
public void IncrementRef(ref int x) { x++; // 修改原始值 } // 调用 int num = 5; IncrementRef(ref num); Console.WriteLine(num); // 输出 6
Java没有直接等效的引用参数,但可以通过包装类或数组实现类似效果。
输出参数(out 关键字):
public bool TryParse(string s, out int result) { return int.TryParse(s, out result); } // 调用 if (TryParse("123", out int number)) { Console.WriteLine($"Parsed number: {number}"); }
Java没有直接等效的输出参数。
参数数组(params 关键字):
public int Sum(params int[] numbers) { return numbers.Sum(); } // 调用 int total = Sum(1, 2, 3, 4, 5);
Java使用可变参数(varargs)实现类似功能:
public int sum(int... numbers) { return Arrays.stream(numbers).sum(); }
方法重载
C#和Java都支持方法重载,允许在同一个类中定义多个同名但参数列表不同的方法:
public class Calculator { public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } }
public int Add(int a, int b) => a + b; public string GetFullName(string firstName, string lastName) => $"{firstName} {lastName}";
Java不支持这种语法糖。
本地函数
C# 7.0引入了本地函数,允许在方法内定义函数:
public int Factorial(int n) { int LocalFactorial(int x) { return x <= 1 ? 1 : x * LocalFactorial(x - 1); } return LocalFactorial(n); }
Java不直接支持本地函数,但可以使用匿名内部类或lambda表达式来实现类似功能。
异步方法
C#对异步编程的支持非常强大,使用async和await关键字:
public async Task FetchDataAsync(string url) { using var client = new HttpClient(); return await client.GetStringAsync(url); } // 调用 string data = await FetchDataAsync("https://api.example.com");
public static class StringExtensions { public static bool IsNullOrEmpty(this string str) { return string.IsNullOrEmpty(str); } } // 使用 string name = "Alice"; bool isEmpty = name.IsNullOrEmpty();
Java不支持扩展方法,但可以使用静态工具类来实现类似功能。
泛型方法
C#和Java都支持泛型方法,允许你编写可以处理多种类型的方法:
public T Max(T a, T b) where T : IComparable { return a.CompareTo(b) > 0 ? a : b; } // 使用 int maxInt = Max(5, 10); string maxString = Max("apple", "banana");
Java的泛型方法语法略有不同:
public > T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; }
方法组合与函数式编程
C#对函数式编程有很好的支持,可以轻松组合和传递方法:
Func square = x => x * x; Func addOne = x => x + 1; Func squareThenAddOne = x => addOne(square(x)); int result = squareThenAddOne(5); // 26
Java也支持函数式编程,但语法略有不同:
Function square = x -> x * x; Function addOne = x -> x + 1; Function squareThenAddOne = square.andThen(addOne); int result = squareThenAddOne.apply(5); // 26
public class Person { public string Name { get; set; } public int Age { get; set; } public void SayHello() { Console.WriteLine($"Hello, my name is {Name} and I'm {Age} years old."); } }
Java中的等效代码:
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void sayHello() { System.out.println("Hello, my name is " + name + " and I'm " + age + " years old."); } }
主要区别:
C#使用属性(Properties)代替了Java的getter和setter方法。
C#的方法名通常使用PascalCase,而Java使用camelCase。
构造函数
C#和Java的构造函数定义类似:
public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int age) { Name = name; Age = age; } }
C#特有的特性:
构造函数初始化器:
public class Employee : Person { public string Company { get; set; } public Employee(string name, int age, string company) : base(name, age) { Company = company; } }
主构造函数(C# 9.0+):
public class Person(string name, int age) { public string Name { get; set; } = name; public int Age { get; set; } = age; }
属性
C#的属性是一个强大的特性,可以替代Java中的getter和setter方法:
public class Person { private string name; public string Name { get { return name; } set { name = value; } } // 自动实现的属性 public int Age { get; set; } // 只读属性 public bool IsAdult => Age >= 18; }
C# 6.0+引入了更简洁的属性语法:
public class Person { public string Name { get; set; } = "John Doe"; public int Age { get; set; } public bool IsAdult => Age >= 18; }
静态成员
C#和Java都支持静态成员:
public class MathHelper { public static double PI = 3.14159; public static int Add(int a, int b) { return a + b; } } // 使用 double pi = MathHelper.PI; int sum = MathHelper.Add(5, 3);
继承
C#和Java的继承语法略有不同:
public class Animal { public virtual void MakeSound() { Console.WriteLine("The animal makes a sound"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("The dog barks"); } }
Java中的等效代码:
public class Animal { public void makeSound() { System.out.println("The animal makes a sound"); } } public class Dog extends Animal { @Override public void makeSound() { System.out.println("The dog barks"); } }
public interface IAnimal { void MakeSound(); void Move() => Console.WriteLine("The animal moves"); } public class Dog : IAnimal { public void MakeSound() { Console.WriteLine("The dog barks"); } // Move方法使用接口的默认实现 }
var person = new { Name = "Alice", Age = 30 }; Console.WriteLine($"{person.Name} is {person.Age} years old");
Java也支持匿名类,但主要用于创建接口或抽象类的匿名实现。
Record类型(C# 9.0+)
C# 9.0引入了Record类型,用于创建不可变的引用类型:
public record Person(string Name, int Age); var alice = new Person("Alice", 30); var bob = alice with { Name = "Bob" }; // 创建一个新记录,只修改Name
Java 14+引入了类似的Record特性:
public record Person(String name, int age) {}
对象初始化器
C#支持对象初始化器,可以在创建对象时直接设置属性:
var person = new Person { Name = "Alice", Age = 30 };
Java不直接支持这种语法,通常使用建造者模式来实现类似效果。
扩展方法
C#允许为现有类型添加新方法,而不需要修改原始类型:
public static class StringExtensions { public static bool IsNullOrEmpty(this string str) { return string.IsNullOrEmpty(str); } } // 使用 string name = "Alice"; bool isEmpty = name.IsNullOrEmpty();
Java不支持扩展方法,但可以使用静态工具类来实现类似功能,或者使用manifold 插件支持。
部分类(Partial Classes)
C#支持部分类,允许将一个类的定义分散到多个文件中:
// File1.cs public partial class MyClass { public void Method1() { } } // File2.cs public partial class MyClass { public void Method2() { } }
Java不支持部分类的概念。
索引器(Indexers)
C#支持索引器,允许类像数组一样通过索引访问:
public class StringCollection { private List items = new List(); public string this[int index] { get { return items[index]; } set { items[index] = value; } } } // 使用 var collection = new StringCollection(); collection[0] = "Hello"; Console.WriteLine(collection[0]); // 输出: Hello
Java没有直接等效的特性,通常需要定义专门的get和set方法。
运算符重载
C#允许为自定义类型定义运算符的行为:
public struct Complex { public double Real { get; set; } public double Imaginary { get; set; } public static Complex operator +(Complex c1, Complex c2) { return new Complex { Real = c1.Real + c2.Real,Imaginary = c1.Imaginary + c2.Imaginary }; } } // 使用 var c1 = new Complex { Real = 1, Imaginary = 2 }; var c2 = new Complex { Real = 3, Imaginary = 4 }; var result = c1 + c2;
Java不支持运算符重载。
嵌套类型
C#和Java都支持嵌套类型,但C#的访问规则更加灵活:
public class OuterClass { private int outerField = 10; public class InnerClass { public void AccessOuterField(OuterClass outer) { Console.WriteLine(outer.outerField); } } }
public sealed class FinalClass { // 这个类不能被继承 } public class BaseClass { public virtual void VirtualMethod() { } } public class DerivedClass : BaseClass { public sealed override void VirtualMethod() { } // 这个方法不能在子类中被重写 }
public class Person { public string Name { get; private set; } public Person(string name) { Name = name; } }
Java不支持这种细粒度的访问控制。
init only setters(C#9.0+)
C#9.0引入了init only setters,允许在对象初始化时设置属性值,而之后这些属性是不可变的:
var circle = new Circle { Radius = 5 }; var bigCircle = new Circle { Diameter = 20 }; public class Circle { private double _radius; public double Radius { get => _radius; init => _radius = value; } public double Diameter { get => 2 * _radius; init => _radius = value / 2; } }
Java没有直接等效的特性。
顶级语句(C# 9.0+)
从C# 9.0开始,可以在文件级别直接编写代码,而不需要显式的Main方法:
Console.WriteLine("Hello, World!");
这个特性简化了小型程序和脚本的编写。Java仍然需要一个包含main方法的类。
模式匹配(C# 7.0+)
C#支持高级的模式匹配,可以在switch语句和is表达式中使用:
object obj = "Hello"; if (obj is string s && s.Length > 5) { Console.WriteLine($"It's a long string: {s}"); } var result = obj switch { string s => $"It's a string: {s}", int i => $"It's an int: {i}", _ => "It's something else" };
public class Animal { public string Name { get; set; } public virtual void MakeSound() { Console.WriteLine("The animal makes a sound"); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("The dog barks"); } }
Java:
public class Animal { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void makeSound() { System.out.println("The animal makes a sound"); } } public class Dog extends Animal { @Override public void makeSound() { System.out.println("The dog barks"); } }
public class Animal { public string Name { get; set; } public Animal(string name) { Name = name; } } public class Dog : Animal { public string Breed { get; set; } public Dog(string name, string breed) : base(name) { Breed = breed; } }
Java中的等效代码:
public class Animal { private String name; public Animal(String name) { this.name = name; } } public class Dog extends Animal { private String breed; public Dog(String name, String breed) { super(name); this.breed = breed; } }
密封类和方法
C#使用sealed关键字来防止类被继承或方法被重写:
public sealed class FinalClass { // 这个类不能被继承 } public class BaseClass { public virtual void VirtualMethod() { } } public class DerivedClass : BaseClass { public sealed override void VirtualMethod() { }// 这个方法不能在子类中被重写 }
Java使用final关键字实现类似功能:
public final class FinalClass { // 这个类不能被继承 } public class BaseClass { public void virtualMethod() { } } public class DerivedClass extends BaseClass { @Override public final void virtualMethod() { } // 这个方法不能在子类中被重写 }
抽象类和方法
C#和Java都支持抽象类和方法:
C#:
public abstract class Shape { public abstract doubleCalculateArea(); } public class Circle : Shape { public double Radius { get; set; } public override double CalculateArea() { return Math.PI * Radius * Radius; } }
Java:
public abstract class Shape { public abstract double calculateArea(); } public class Circle extends Shape { private double radius; public void setRadius(double radius) { this.radius = radius; } @Override public double calculateArea() { return Math.PI * radius * radius; } }
接口
C#和Java都支持接口,但C# 8.0+允许接口定义默认实现:
C#:
public interface IDrawable { void Draw(); void Erase() => Console.WriteLine("Default erase behavior"); } public class Square : IDrawable { public void Draw() { Console.WriteLine("Drawing a square"); }// Erase方法使用默认实现 }
Java 8+也支持接口默认方法:
public interface Drawable { void draw(); default void erase() { System.out.println("Default erase behavior"); } } public class Square implements Drawable { @Override public void draw() { System.out.println("Drawing a square"); } // erase方法使用默认实现 }
多重继承
C#和Java都不支持类的多重继承,但都允许一个类实现多个接口:
public interface IDrawable { void Draw(); } public interface IResizable { void Resize(int width, int height); } public class Rectangle : IDrawable, IResizable { public void Draw() { Console.WriteLine("Drawing a rectangle"); } public void Resize(int width, int height) { Console.WriteLine($"Resizing to {width}x{height}"); } }
泛型约束
C#支持泛型约束,可以限制泛型参数的类型:
public class GenericRepository where T : class, new() { public T CreateNew() { return new T(); } }
Java也支持泛型约束,但语法略有不同:
public class GenericRepository { public T createNew() throws InstantiationException, IllegalAccessException { return T.class.newInstance(); } }
协变和逆变
C#支持泛型接口和委托的协变和逆变:
public interface IEnumerable { /* ... */ } public interface IComparer { /* ... */ } IEnumerable strings = new List(); IEnumerable
Java也支持泛型的协变和逆变,但语法不同:
List strings = new ArrayList<>(); List extends Object> objects = strings; // 协变 Comparator objectComparator = /* ... */; Comparator super String> stringComparator = objectComparator; // 逆变
隐藏基类成员
C#使用new关键字来隐藏基类成员:
public class BaseClass { public void Method() { Console.WriteLine("BaseClass.Method"); } } public class DerivedClass : BaseClass { public new void Method() { Console.WriteLine("DerivedClass.Method"); } }
Java没有直接等效的语法,但可以通过重新定义方法来实现类似效果。
基类访问
C#使用base关键字来访问基类成员,类似于Java中的super:
public class DerivedClass : BaseClass { public override void Method() { base.Method(); // 调用基类方法 Console.WriteLine("Additional behavior in derived class"); } }
构造函数链
C#允许在一个类中定义多个构造函数,并使用this关键字链接它们:
public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name) : this(name, 0) { } public Person(string name, int age) { Name = name; Age = age; } }
显式接口实现
C#允许显式实现接口方法,这在实现多个具有相同方法签名的接口时特别有用:
public interfaceIA { void Method(); } public interface IB { void Method(); } public class MyClass : IA, IB { void IA.Method() { Console.WriteLine("IA.Method"); } void IB.Method() { Console.WriteLine("IB.Method"); } public void Method() { Console.WriteLine("MyClass.Method"); } }
Java不支持这种显式接口实现。
抽象属性
C#允许在抽象类中定义抽象属性:
public abstract class Animal { public abstract string Sound { get; } } public class Dog : Animal { public override string Sound => "Woof"; }
派生类中的新成员
C#允许在派生类中添加新的成员,而不需要特殊语法:
public class Animal { public virtual void MakeSound() { } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("Woof"); } public void Fetch() //新方法 { Console.WriteLine("Dog is fetching"); } }
泛型继承
C#支持泛型类的继承,并允许在派生类中指定或保持开放泛型类型参数:
public class GenericBase { public T Data { get; set; } } public class StringDerived : GenericBase { public void PrintUpperCase() { Console.WriteLine(Data.ToUpper()); } } public class GenericDerived : GenericBase { public void PrintType() { Console.WriteLine(typeof(T).Name); } }
接口的多重继承虽然C#不支持类的多重继承,但接口可以继承多个接口:
public interfaceIA { void MethodA(); } public interface IB { void MethodB(); } public interface IC : IA, IB { void MethodC(); } public class MyClass : IC { public void MethodA() { } public void MethodB() { } public void MethodC() { } }
继承链中的构造函数调用顺序
在C#中,当创建一个派生类的实例时,构造函数的调用顺序是从最基础的类开始,一直到最派生的类:
public class A { public A() { Console.WriteLine("A"); } } public class B : A { public B() { Console.WriteLine("B"); } } public class C : B { public C() { Console.WriteLine("C"); } } // 使用 var c = new C(); // 输出: A B C
public abstract class Animal { protected string name; public Animal(string name) { this.name = name; } public abstract void MakeSound(); } public interface IMovable { void Move(); } public class Dog : Animal, IMovable { public Dog(string name) : base(name) { } public override void MakeSound() { Console.WriteLine("Woof!"); } public void Move() { Console.WriteLine("Dog is running"); } }
多接口继承与默认实现
C# 8.0引入的接口默认实现允许我们在不破坏现有代码的情况下向接口添加新方法:
public interface ILogger { void Log(string message);void LogError(string message) => Log($"ERROR: {message}"); } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } // 可以选择重写或使用默认的LogError实现 }
泛型约束中的继承
C#允许在泛型约束中使用继承关系:
public class Animal { } public class Dog : Animal { } public class Kennel where T : Animal { public void AddAnimal(T animal) { } } var dogKennel = new Kennel(); // 有效 // var intKennel = new Kennel(); // 编译错误
隐藏继承成员
有时你可能想在派生类中隐藏基类的成员,而不是覆盖它。C#使用new关键字来实现这一点:
public class Base { public virtual void Method() { Console.WriteLine("Base.Method"); } } public class Derived : Base { public new void Method() { Console.WriteLine("Derived.Method"); } } Base b = new Derived(); b.Method(); // 输出: Base.Method Derived d = new Derived(); d.Method(); // 输出: Derived.Method
协变和逆变在委托中的应用
C#支持在委托中使用协变和逆变:
delegate T Factory(); delegate void Action(T obj); class Animal { } class Dog : Animal { } class Program { static Dog CreateDog() => new Dog(); static void HandleAnimal(Animal a) { } static void Main() { Factory dogFactory = CreateDog; Factory animalFactory = dogFactory; // 协变 Action animalHandler = HandleAnimal; Action dogHandler = animalHandler; // 逆变 } }
虚拟属性和索引器
C#允许将属性和索引器声明为虚拟的,这样它们可以在派生类中被重写:
public class Base { public virtual int Property { get; set; } public virtual int this[int index] { get =>0; set { } } } public class Derived : Base { private int[] data = new int[10]; public override int Property { get => base.Property; set => base.Property = value * 2; } public override int this[int index] { get => data[index]; set => data[index] = value; } }
抽象类中的密封方法
虽然看起来有点矛盾,但C#允许在抽象类中定义密封方法。这可以用来提供一个不能被重写的实现:
public abstract class Base { public abstract void AbstractMethod(); public virtual void VirtualMethod() { } public sealed void SealedMethod() { } } public class Derived : Base { public override void AbstractMethod() { } public override void VirtualMethod() { } // 可以重写 //无法重写SealedMethod }
接口中的静态成员
从C# 8.0开始,接口可以包含静态成员,包括字段、方法、属性等:
public interface IMyInterface { static int MyProperty { get; set; } static void MyMethod() => Console.WriteLine("Hello from interface!"); }
继承链中的构造函数和字段初始化顺序
了解C#中构造函数和字段初始化的顺序非常重要:
派生类的字段初始化器
派生类的构造函数
基类的字段初始化器
基类的构造函数
public class Base { public int BaseField = 1; public Base() { Console.WriteLine($"Base constructor, BaseField = {BaseField}"); } } public class Derived : Base { public int DerivedField = BaseField + 1; public Derived() { Console.WriteLine($"Derived constructor, DerivedField = {DerivedField}"); } } // 输出: // Base constructor, BaseField = 1 // Derived constructor, DerivedField = 2
使用接口作为类型参数
接口可以用作方法的类型参数,这提供了更大的灵活性:
public void ProcessItems(IEnumerable items) where T : IComparable { foreach (var item in items.OrderBy(i => i)) { Console.WriteLine(item); } }
public interface IButton { void Paint(); } public interface ICheckbox { void Paint(); } public interface IGUIFactory { IButton CreateButton(); ICheckbox CreateCheckbox(); } public class WinButton : IButton { public void Paint() => Console.WriteLine("Windows Button"); } public class MacButton : IButton { public void Paint() => Console.WriteLine("Mac Button"); } public class WinCheckbox : ICheckbox { public void Paint() => Console.WriteLine("Windows Checkbox"); } public class MacCheckbox : ICheckbox { public void Paint() => Console.WriteLine("Mac Checkbox"); } public class WinFactory : IGUIFactory { public IButton CreateButton() => new WinButton(); public ICheckbox CreateCheckbox() => new WinCheckbox(); } public class MacFactory : IGUIFactory { public IButton CreateButton() => new MacButton(); public ICheckbox CreateCheckbox() => new MacCheckbox(); } // 使用 IGUIFactory factory = new WinFactory(); var button = factory.CreateButton(); button.Paint(); //输出: Windows Button
模板方法模式
模板方法模式定义了一个算法的骨架,允许子类重新定义算法的某些步骤,而不改变算法的结构:
public abstract class DataProcessor { public void ProcessData() { OpenFile(); ExtractData(); ParseData(); AnalyzeData(); SendReport(); CloseFile(); } protected abstract void ExtractData(); protected abstract void ParseData(); protected abstract void AnalyzeData(); protected virtual void OpenFile() { Console.WriteLine("Opening file..."); } protected virtual void SendReport() { Console.WriteLine("Sending report..."); } protected virtual void CloseFile() { Console.WriteLine("Closing file..."); } } public class PDFProcessor : DataProcessor { protected override void ExtractData() { Console.WriteLine("Extracting data from PDF..."); } protected override void ParseData() { Console.WriteLine("Parsing PDF data..."); } protected override void AnalyzeData() { Console.WriteLine("Analyzing PDF data..."); } }
策略模式
策略模式定义了一系列算法,并使它们可以互相替换。这个模式利用了多态性:
public interface ISortStrategy { void Sort(List data); } public class BubbleSort : ISortStrategy { public void Sort(List data) { Console.WriteLine("Performing bubble sort"); // 实现冒泡排序 } } public class QuickSort : ISortStrategy { public void Sort(List data) { Console.WriteLine("Performing quick sort"); // 实现快速排序 } } public class Sorter { private ISortStrategy _strategy; public Sorter(ISortStrategy strategy) { _strategy = strategy; } public void SetStrategy(ISortStrategy strategy) { _strategy = strategy; } public void SortData(List data) { _strategy.Sort(data); } } // 使用 var sorter = new Sorter(new BubbleSort()); sorter.SortData(new List { 3, 1, 4, 1, 5, 9}); sorter.SetStrategy(new QuickSort()); sorter.SortData(new List { 3, 1, 4, 1, 5, 9 });
装饰器模式
装饰器模式允许你动态地给对象添加新的行为,而不改变其结构:
public interface ICoffee { string GetDescription(); double GetCost(); } public class SimpleCoffee : ICoffee { public string GetDescription() => "Simple Coffee"; public double GetCost() => 1; } public abstract class CoffeeDecorator : ICoffee { protected ICoffee _coffee; public CoffeeDecorator(ICoffee coffee) { _coffee = coffee; } public virtual string GetDescription() => _coffee.GetDescription(); public virtual double GetCost() => _coffee.GetCost(); } public class MilkDecorator : CoffeeDecorator { public MilkDecorator(ICoffee coffee) : base(coffee) { } public override string GetDescription() => $"{base.GetDescription()}, Milk"; public override double GetCost() => base.GetCost() + 0.5; } public class SugarDecorator : CoffeeDecorator { public SugarDecorator(ICoffee coffee) : base(coffee) { } public override string GetDescription() => $"{base.GetDescription()}, Sugar"; public override double GetCost() => base.GetCost() + 0.2; } // 使用 ICoffee coffee = new SimpleCoffee(); coffee = new MilkDecorator(coffee); coffee = new SugarDecorator(coffee); Console.WriteLine($"Description: {coffee.GetDescription()}"); Console.WriteLine($"Cost: ${coffee.GetCost()}");
使用协变和逆变优化接口设计
协变和逆变可以使得泛型接口更加灵活:
// 协变接口 public interface IProducer { T Produce(); } // 逆变接口 public interface IConsumer { void Consume(T item); } public class Animal { } public class Dog : Animal { } public class DogProducer : IProducer { public Dog Produce() => new Dog(); } public class AnimalConsumer : IConsumer { public void Consume(Animal animal) { } } // 使用 IProducer animalProducer = new DogProducer(); // 协变 IConsumer dogConsumer = new AnimalConsumer(); // 逆变
使用接口隔离原则(ISP)
接口隔离原则建议将大接口分割成更小、更具体的接口:
// 不好的设计 public interface IWorker { void Work(); void Eat();void Sleep(); } // 更好的设计 public interface IWorkable { void Work(); } public interface IEatable { void Eat(); } public interface ISleepable { void Sleep(); } public class Human : IWorkable, IEatable, ISleepable { public void Work() { } public void Eat() { } public void Sleep() { } } public class Robot : IWorkable { public void Work() { } }
使用组合优于继承虽然继承是一个强大的工具,但它也可能导致紧耦合。
在许多情况下,组合可能是一个更好的选择:
// 使用继承 public class Bird { public virtual void Fly() { Console.WriteLine("Flying..."); } } public class Penguin : Bird { public override void Fly() { throw new NotSupportedException("Penguins can't fly"); } } // 使用组合 public interface IFlyable { void Fly(); } public class FlyingBehavior : IFlyable { public void Fly() { Console.WriteLine("Flying..."); } } public class Bird { private IFlyable _flyingBehavior; public Bird(IFlyable flyingBehavior) { _flyingBehavior = flyingBehavior; } public void Fly() { _flyingBehavior.Fly(); } } public class Penguin : Bird { public Penguin() : base(new NullFlyingBehavior()) { } } public class NullFlyingBehavior : IFlyable { public void Fly() { } // Do nothing }
public interface IAnimal { string Name { get; set; } void MakeSound(); } public class Dog : IAnimal { public string Name { get; set; } public void MakeSound() { Console.WriteLine("Woof!"); } }
C# 8.0中的接口新特性:
默认实现:
public interface IAnimal { string Name { get; set; } void MakeSound(); void Eat() => Console.WriteLine($"{Name} is eating."); }
静态成员:
public interface IAnimal { static int Count { get; set; } static void IncrementCount() => Count++; }
私有成员:
public interface IAnimal { private static int count; public static int Count => count;static void IncrementCount() => count++; }
public abstract class Animal { public string Name { get; set; } public abstract void MakeSound(); public virtual void Eat() { Console.WriteLine($"{Name} is eating."); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("Woof!"); } }
public interface IPayable { decimalCalculatePay(); } public abstract class Employee : IPayable { public string Name { get; set; } public int Id { get; set; } protected Employee(string name, int id) { Name = name; Id = id; } public abstract decimal CalculatePay(); public virtual void DisplayInfo() { Console.WriteLine($"Employee: {Name}, ID: {Id}"); } } public class FullTimeEmployee : Employee { public decimal MonthlySalary { get; set; } public FullTimeEmployee(string name, int id, decimal monthlySalary) : base(name, id) { MonthlySalary = monthlySalary; } public override decimal CalculatePay() { return MonthlySalary; } } public class PartTimeEmployee : Employee { public decimal HourlyRate { get; set; } public int HoursWorked { get; set; } public PartTimeEmployee(string name, int id, decimal hourlyRate) : base(name, id) { HourlyRate = hourlyRate; } public override decimal CalculatePay() { return HourlyRate * HoursWorked; } public override void DisplayInfo() { base.DisplayInfo(); Console.WriteLine($"Hourly Rate: {HourlyRate}"); } } public class Contractor : IPayable { public string Name { get; set; } public decimal ContractAmount { get; set; } public Contractor(string name, decimal contractAmount) { Name = name; ContractAmount = contractAmount; } public decimal CalculatePay() { return ContractAmount; } } // 使用示例 public class PayrollSystem { public void ProcessPayroll(List payables) { foreach (var payable in payables) { Console.WriteLine($"Paying {payable.CalculatePay():C}"); if (payable is Employee employee) { employee.DisplayInfo(); }} } } // 主程序 class Program { static void Main(string[] args) { var payables = new List { new FullTimeEmployee("Alice", 1, 5000m), new PartTimeEmployee("Bob", 2, 20m) { HoursWorked = 80 }, new Contractor("Charlie", 3000m) }; var payrollSystem = new PayrollSystem(); payrollSystem.ProcessPayroll(payables); } }
public interface IUserRepository { Task GetUserByIdAsync(int id); Task> GetAllUsersAsync(); Task AddUserAsync(User user); } public class UserRepository : IUserRepository { private readonly ApplicationDbContext _context; public UserRepository(ApplicationDbContext context) { _context = context; } public async Task GetUserByIdAsync(int id) { return await _context.Users.FindAsync(id); } public async Task> GetAllUsersAsync() { return await _context.Users.ToListAsync(); } public async Task AddUserAsync(User user) { await _context.Users.AddAsync(user); await _context.SaveChangesAsync(); } } // 在Startup.cs中注册服务 public void ConfigureServices(IServiceCollection services) { services.AddScoped(); } // 在控制器中使用 public class UserController :ControllerBase { private readonly IUserRepository _userRepository; public UserController(IUserRepository userRepository) { _userRepository = userRepository; } [HttpGet("{id}")] public async Task> GetUser(int id) { var user = await _userRepository.GetUserByIdAsync(id); if (user == null) { return NotFound(); } return user; } }
这种方法允许我们轻松地替换实现,例如,我们可以创建一个模拟版本用于测试:
public class MockUserRepository : IUserRepository { private readonly List _users = new(); public async Task GetUserByIdAsync(int id) { return await Task.FromResult(_users.FirstOrDefault(u => u.Id == id)); } // 实现其他方法... } // 在测试中使用 [Fact] public async Task GetUser_ReturnsUser_WhenUserExists() { // Arrange var mockRepo = new MockUserRepository(); var controller = new UserController(mockRepo); var testUser = new User { Id = 1, Name = "Test User" }; await mockRepo.AddUserAsync(testUser); // Act var result = await controller.GetUser(1); // Assert var actionResult = Assert.IsType>(result); var returnValue = Assert.IsType(actionResult.Value); Assert.Equal(testUser.Id, returnValue.Id); Assert.Equal(testUser.Name, returnValue.Name); }
2. 抽象类作为领域模型的基类
在领域驱动设计(DDD)中,抽象类常常用作实体或值对象的基类。
public abstract class Entity { public int Id { get; protected set; } public override bool Equals(object obj) { var other = obj as Entity; if (ReferenceEquals(other, null)) return false; if (ReferenceEquals(this, other)) return true; if (GetType() != other.GetType()) return false; if (Id ==0 || other.Id == 0) return false; return Id == other.Id; } public static bool operator ==(Entity a, Entity b) { if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) return true; if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false; return a.Equals(b); } public static bool operator !=(Entity a, Entity b) { return !(a == b); } public override int GetHashCode() { return (GetType().ToString() + Id).GetHashCode(); } } public class User : Entity { public string Name { get; set; } public string Email { get; set; } } public class Product : Entity { public string Name { get; set; } public decimal Price { get; set; } }
这个抽象基类提供了通用的相等性比较逻辑,所有继承自它的实体都会自动获得这些功能。
3. 接口默认实现的高级应用
C# 8.0引入的接口默认实现可以用于创建可选功能或提供默认行为。
public interface ILogger { void Log(string message); void LogError(string message) => Log($"ERROR: {message}"); void LogWarning(string message) => Log($"WARNING: {message}"); void LogInfo(string message) => Log($"INFO: {message}"); } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine($"[{DateTime.Now}] {message}"); } // 可以选择性地重写默认实现 public void LogError(string message) { Console.ForegroundColor = ConsoleColor.Red; Log($"ERROR: {message}"); Console.ResetColor(); } } public class FileLogger : ILogger { private readonly string _path; public FileLogger(string path) { _path = path; } public void Log(string message) { File.AppendAllText(_path, $"[{DateTime.Now}] {message}\n"); } // 使用默认实现的其他方法 }
这种方法允许我们在接口中定义通用行为,同时保留了实现类专它的灵活性。
最佳实践和设计模式
1. 策略模式
策略模式是一个典型的使用接口的设计模式:
public interface IPaymentStrategy { void Pay(decimal amount); } public class CreditCardPayment : IPaymentStrategy { public void Pay(decimal amount) { Console.WriteLine($"Paying {amount} using Credit Card"); } } public class PayPalPayment : IPaymentStrategy { public void Pay(decimal amount) { Console.WriteLine($"Paying {amount} using PayPal"); } } public class ShoppingCart { private readonly IPaymentStrategy _paymentStrategy; public ShoppingCart(IPaymentStrategy paymentStrategy) { _paymentStrategy = paymentStrategy; } public void Checkout(decimal amount) { _paymentStrategy.Pay(amount); } } // 使用 var cart = new ShoppingCart(new CreditCardPayment()); cart.Checkout(100.50m); cart = new ShoppingCart(new PayPalPayment()); cart.Checkout(200.75m);
2. 模板方法模式
模板方法模式是抽象类的一个经典应用:
public abstract class PizzaMaker { public void MakePizza() { PrepareDough(); AddSauce(); AddToppings();Bake(); Cut(); } protected abstract void AddToppings(); protected virtual void PrepareDough() { Console.WriteLine("Preparing pizza dough"); } protected virtual void AddSauce() { Console.WriteLine("Adding tomato sauce"); } protected virtual void Bake() { Console.WriteLine("Baking for 15 minutes at 200°C"); } protected virtual void Cut() { Console.WriteLine("Cutting the pizza into 8 slices"); } } public class MargheritaPizza : PizzaMaker { protected override void AddToppings() { Console.WriteLine("Adding mozzarella and basil"); } } public class PepperoniPizza : PizzaMaker { protected override void AddToppings() { Console.WriteLine("Adding pepperoni and extra cheese"); } protected override void Bake() { Console.WriteLine("Baking for 20 minutes at 180°C"); } } // 使用 var margherita = new MargheritaPizza(); margherita.MakePizza(); var pepperoni = new PepperoniPizza(); pepperoni.MakePizza();
高级技巧
使用泛型约束与接口:
public interface IEntity { int Id { get; set; } } public interface IRepository where T : class, IEntity { T GetById(int id); void Add(T entity); void Update(T entity); void Delete(int id); } public class GenericRepository : IRepository where T : class, IEntity { private readonly DbContext _context; private readonly DbSet _dbSet; public GenericRepository(DbContext context) { _context = context; _dbSet = context.Set(); } public T GetById(int id) { return _dbSet.Find(id); } public void Add(T entity) { _dbSet.Add(entity); _context.SaveChanges(); } public void Update(T entity) { _dbSet.Attach(entity); _context.Entry(entity).State = EntityState.Modified; _context.SaveChanges(); } public void Delete(int id) { var entity = GetById(id); _dbSet.Remove(entity); _context.SaveChanges(); } }
使用接口进行多重继承:
public interface IFlying { void Fly(); } public interface ISwimming { void Swim(); } public class Duck : IFlying, ISwimming { public void Fly() { Console.WriteLine("Duck is flying"); } public void Swim() { Console.WriteLine("Duck is swimming"); } } public class FlyingFish : IFlying, ISwimming { public void Fly() { Console.WriteLine("Flying fish is gliding through the air"); } public void Swim() { Console.WriteLine("Flying fish is swimming"); } }
使用显式接口实现来解决方法名冲突:
public interface IWork { void Start(); } public interface IEngine { void Start(); } public class Car : IWork, IEngine { void IWork.Start() { Console.WriteLine("Starting work"); } void IEngine.Start() { Console.WriteLine("Starting engine"); } public void Start() { Console.WriteLine("Car is starting"); } } // 使用 Car car = new Car(); car.Start(); // 输出: Car is starting IWork work = car; work.Start(); // 输出: Starting work IEngine engine = car; engine.Start(); // 输出: Starting engine
public class AnonymousAndLambdaDemo { public void Run() { // 匿名方法 SimpleDelegate anonymousDelegate = delegate(string message) { Console.WriteLine($"Anonymous: {message}"); }; // Lambda 表达式 SimpleDelegate lambdaDelegate = (message) => Console.WriteLine($"Lambda: {message}"); anonymousDelegate("Hello from anonymous method!"); lambdaDelegate("Hello from lambda expression!"); } }
1.9.2 事件(Events)
事件提供了一种方式,使得一个类可以通知其他类发生了某些事情。事件使用委托来实现。
基本语法
public class Publisher { // 定义一个委托类型 public delegate void EventHandler(string message); // 声明一个事件 public event EventHandler SomethingHappened; public void DoSomething() { // 触发事件 SomethingHappened?.Invoke("Something just happened!"); } } public class Subscriber { public void HandleEvent(string message) { Console.WriteLine($"Event handled: {message}"); } } // 使用 var publisher = new Publisher(); var subscriber = new Subscriber(); publisher.SomethingHappened += subscriber.HandleEvent; publisher.DoSomething();// 输出: Event handled: Something just happened!
public class CustomEventArgs : EventArgs { public string Message { get; set; } } public class StandardEventPublisher { public event EventHandler CustomEvent; protected virtual void OnCustomEvent(CustomEventArgs e) { CustomEvent?.Invoke(this, e); } public void TriggerEvent() { OnCustomEvent(new CustomEventArgs { Message = "Custom event triggered" }); } } public class StandardEventSubscriber { public void HandleCustomEvent(object sender, CustomEventArgs e) { Console.WriteLine($"Event received: {e.Message}"); } } // 使用 var publisher = new StandardEventPublisher(); var subscriber = new StandardEventSubscriber(); publisher.CustomEvent += subscriber.HandleCustomEvent; publisher.TriggerEvent(); // 输出: Event received: Custom event triggered
1.9.3 委托和事件的高级应用
1. 异步编程
委托在异步编程中扮演重要角色,尽管现在更常用的是 Task-based 异步模式。
public class AsyncDelegateDemo { public delegate int AsyncCalculation(int x, int y); public static int Add(int x, int y) { Thread.Sleep(1000); // 模拟耗时操作 return x + y; } public async Task RunAsync() { AsyncCalculation del = Add; var result = await Task.Run(() => del(5, 3)); Console.WriteLine($"Async result: {result}"); } }
2. LINQ 和函数式编程
委托是LINQ和函数式编程风格的基础。
public class LinqDemo { public void Run() { var numbers = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var evenNumbers = numbers.Where(n => n % 2 == 0).Select(n => n * n).ToList(); Console.WriteLine(string.Join(", ", evenNumbers)); } }
3. 事件聚合器
在复杂的应用程序中,可以使用事件聚合器模式来集中管理事件。
public class EventAggregator { private Dictionary> eventHandlers = new Dictionary>(); public void Subscribe(Action handler) { var eventType = typeof(TEvent); if (!eventHandlers.ContainsKey(eventType)) { eventHandlers[eventType] = new List(); } eventHandlers[eventType].Add(handler); } public void Publish(TEvent eventToPublish) { var eventType = typeof(TEvent); if (eventHandlers.ContainsKey(eventType)) { foreach (var handler in eventHandlers[eventType].Cast>()) { handler(eventToPublish); } } } } // 使用 public class EventAggregatorDemo { public class UserLoggedInEvent { public string Username { get; set; } } public void Run() { var aggregator = new EventAggregator(); aggregator.Subscribe(e =>Console.WriteLine($"User logged in: {e.Username}")); aggregator.Publish(new UserLoggedInEvent { Username = "JohnDoe" }); } }
1.10.4 最佳实践
使用EventHandler 和 EventHandler 作为事件委托类型。
总是检查事件是否为null 再调用(使用 ?. 操作符)。
考虑使用 System.Action 和 System.Func 代替自定义委托类型。
在多线程环境中使用事件时要小心,考虑线程安全问题。
避免在循环中频繁触发事件,考虑批处理或节流。
// 好的实践 public event EventHandler GoodEvent; protected virtual void OnGoodEvent(CustomEventArgs e) { GoodEvent?.Invoke(this, e); } // 避免的做法 public delegate void BadEventDelegate(int someParameter); public event BadEventDelegate BadEvent; public void TriggerBadEvent() { if (BadEvent != null) { BadEvent(42); // 不安全,可能在检查和调用之间变为null } }
public class GenericList { private List items = new(); public void Add(T item) => items.Add(item); public T GetItem(int index) => items[index]; } // 使用示例 var intList = new GenericList(); intList.Add(10); Console.WriteLine(intList.GetItem(0)); // 输出: 10 var stringList = new GenericList(); stringList.Add("Hello"); Console.WriteLine(stringList.GetItem(0)); // 输出: Hello
Java示例:
public class GenericList { private List items = new ArrayList<>(); public void add(T item) { items.add(item); } public T getItem(int index) { return items.get(index); } } // 使用示例 GenericList intList = new GenericList<>(); intList.add(10); System.out.println(intList.getItem(0)); // 输出: 10 GenericList stringList = new GenericList<>(); stringList.add("Hello"); System.out.println(stringList.getItem(0)); // 输出: Hello
看起来很相似,对吧?但是,让我们深入探讨一下C#泛型的一些独特特性。
1.10.2 泛型约束
C#允许我们对泛型类型参数添加约束,这在Java中是没有直接对应的功能。
C#示例:
public class Calculator where T : struct, IComparable { public T Max(T a, T b) => a.CompareTo(b) > 0 ? a : b; } // 使用示例 var calc = new Calculator(); Console.WriteLine(calc.Max(5, 10)); // 输出: 10 // 下面的代码会导致编译错误,因为string不是值类型 // var stringCalc = new Calculator();
在这个例子中,我们限制了T必须是值类型(struct)并且实现了IComparable接口。
Java的近似实现:
Java没有直接的泛型约束,但可以通过接口来实现类似的功能:
public class Calculator> { public T max(T a, T b) { return a.compareTo(b) > 0 ? a : b; } } // 使用示例 Calculator calc = new Calculator<>(); System.out.println(calc.max(5, 10)); // 输出: 10 // 这是允许的,因为Java没有值类型的概念 Calculator stringCalc = new Calculator<>(); System.out.println(stringCalc.max("apple", "banana")); // 输出: banana
// 协变 IEnumerable strings = new List(); IEnumerable objects = strings; // 这是允许的 // 逆变 Action objectAction = obj => Console.WriteLine(obj); Action stringAction = objectAction; // 这是允许的 // 同时使用协变和逆变 interface IConverter { TOutput Convert(TInput input); } class StringToIntConverter : IConverter { public int Convert(string input) => int.Parse(input); } IConverter converter = new StringToIntConverter(); int result = converter.Convert("42"); Console.WriteLine(result); // 输出: 42
Java示例:
Java的泛型协变较为有限,主要通过通配符实现:
// 协变(只读) List strings = new ArrayList<>(); List extends Object> objects = strings; // 这是允许的 // Java不支持泛型方法的逆变 // 下面的代码在Java中是不允许的: // Consumer objectConsumer = obj -> System.out.println(obj); // Consumer stringConsumer = objectConsumer; // Java中没有直接对应C#的in和out关键字的概念 interface Converter { R convert(T input); } class StringToIntConverter implements Converter { @Override public Integer convert(String input) { return Integer.parseInt(input); } } // 在Java中,不能像C#那样直接赋值 // Converter converter = new StringToIntConverter(); // 这在Java中是不允许的 Converter converter = new StringToIntConverter(); int result = converter.convert("42"); System.out.println(result); // 输出: 42
1.10.4 泛型方法
C#和Java都支持泛型方法,但C#的语法更加简洁。
C#示例:
public static class Utilities { public static T[] CreateArray(params T[] elements) => elements; public static void Swap(ref T a, ref T b) { T temp = a; a = b; b = temp; } } // 使用示例 int[] numbers = Utilities.CreateArray(1, 2, 3); Console.WriteLine(string.Join(", ", numbers)); // 输出: 1, 2, 3 int x = 5, y = 10; Utilities.Swap(ref x, ref y); Console.WriteLine($"x = {x}, y = {y}"); // 输出: x = 10, y = 5
Java示例:
public class Utilities { @SafeVarargs public static T[] createArray(T... elements) { return elements; } public static void swap(T[] arr, int i, int j) { T temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } // 使用示例 Integer[] numbers = Utilities.createArray(1, 2, 3); System.out.println(Arrays.toString(numbers)); // 输出: [1, 2, 3] Integer[] arr = {5, 10}; Utilities.swap(arr, 0, 1); System.out.println("x = " + arr[0] + ", y = " + arr[1]); // 输出: x = 10, y = 5
注意,Java不支持引用参数,所以我们不能像C#那样直接交换两个变量的值。
1.10.5 默认值和约束
C#允许我们为泛型类型参数指定默认值,这在Java中是不可能的。
C#示例:
public class DefaultValueExample where T : struct { public T GetDefaultValue() => default(T); } // 使用示例 var example = new DefaultValueExample(); Console.WriteLine(example.GetDefaultValue()); // 输出: 0 var dateExample = new DefaultValueExample(); Console.WriteLine(dateExample.GetDefaultValue()); // 输出: 1/1/0001 12:00:00 AM
Java示例:
Java没有直接对应的功能,但可以通过反射或其他方式模拟:
public class DefaultValueExample { private final Class type; public DefaultValueExample(Class type) { this.type = type; } @SuppressWarnings("unchecked") public T getDefaultValue() { if (type.equals(int.class)) return (T) Integer.valueOf(0); if (type.equals(boolean.class)) return (T) Boolean.FALSE; // ... 其他基本类型的处理 return null; // 对于引用类型,返回null } } // 使用示例 DefaultValueExample example = new DefaultValueExample<>(int.class); System.out.println(example.getDefaultValue()); // 输出: 0 DefaultValueExample boolExample = new DefaultValueExample<>(boolean.class); System.out.println(boolExample.getDefaultValue()); // 输出: false
var people = new List { new Person { Name = "Alice", Age = 25, City = "New York" }, new Person { Name = "Bob", Age = 30, City = "London" }, new Person { Name = "Charlie", Age = 35, City = "New York" }, new Person { Name = "David", Age = 40, City = "London" } }; var query = from person in people where person.Age > 30 orderby person.Name group person by person.City into cityGroup select new { City = cityGroup.Key, Count = cityGroup.Count(), AverageAge = cityGroup.Average(p => p.Age) }; foreach (var result in query) { Console.WriteLine($"City: {result.City}, Count: {result.Count}, Average Age: {result.AverageAge}"); } // 输出: // City: London, Count: 1, Average Age: 40 // City: New York, Count: 1, Average Age: 35
Java示例:
Java的Stream API也可以实现类似的功能,但语法会更加复杂:
import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; class Person { String name; int age; String city; //构造函数、getter和setter省略 } List people = Arrays.asList( new Person("Alice", 25, "New York"), new Person("Bob", 30, "London"), new Person("Charlie", 35, "New York"), new Person("David", 40, "London") ); Map> groupedByCity = people.stream() .filter(person -> person.age > 30) .sorted((p1, p2) -> p1.name.compareTo(p2.name)) .collect(Collectors.groupingBy(person -> person.city)); groupedByCity.forEach((city, cityGroup) -> { double averageAge = cityGroup.stream().mapToInt(p -> p.age).average().orElse(0); System.out.printf("City: %s, Count: %d, Average Age: %.2f%n", city, cityGroup.size(), averageAge); }); // 输出: // City: London, Count: 1, Average Age: 40.00 // City: New York, Count: 1, Average Age: 35.00
LINQ的一个强大特性是它可以针对不同的数据源使用相同的查询语法。例如,LINQ to SQL允许我们使用LINQ语法直接查询数据库。
C#示例(使用Entity Framework Core):
using (var context = new MyDbContext()) { var query = from user in context.Users where user.Age > 18 orderby user.Name select new { user.Name, user.Age }; foreach (var item in query) { Console.WriteLine($"Name: {item.Name}, Age: {item.Age}"); } }
EntityManager em = // 获取EntityManager TypedQuery query = em.createQuery( "SELECT NEW com.example.UserDTO(u.name, u.age) " + "FROM User u WHERE u.age >18 ORDER BY u.name", User.class); List results = query.getResultList(); for (UserDTO user : results) { System.out.println("Name: " + user.getName() + ", Age: " + user.getAge()); }
1.11.5 LINQ的其他特性
LINQ还有许多其他强大的特性,例如:
即时查询(使用ToList()、ToArray()等方法)
自定义查询操作符
并行LINQ(PLINQ)用于并行查询处理
C#示例(PLINQ):
var numbers = Enumerable.Range(1, 1000000); var evenSquares = numbers.AsParallel().Where(n => n % 2 == 0) .Select(n => n * n); Console.WriteLine($"Count of even squares: {evenSquares.Count()}");
Java示例(并行流):
import java.util.stream.IntStream; long count = IntStream.range(1, 1000001) .parallel() .filter(n -> n % 2 == 0) .mapToLong(n -> (long)n * n) .count(); System.out.println("Count of even squares: " + count);
public async Task ProcessMultipleAsync() { var task1 = FetchDataAsync("url1"); var task2 = FetchDataAsync("url2"); var task3 = FetchDataAsync("url3"); var results = await Task.WhenAll(task1, task2, task3); foreach (var result in results) { Console.WriteLine(result); } }
try { // 可能抛出异常的代码 } catch (Exception ex) when (ex is FileNotFoundException fnf) { Console.WriteLine($"文件未找到:{fnf.FileName}"); } catch (Exception ex) when (ex is UnauthorizedAccessException uae) { Console.WriteLine($"访问被拒绝:{uae.Message}"); }
这种方式比Java的instanceof检查更加简洁和强大。
1.13.4 抛出异常
C#和Java在抛出异常方面非常相似,都使用throw关键字。
C#示例:
if (someCondition) { throw new ArgumentException("无效的参数"); }
Java示例:
if (someCondition) { throw new IllegalArgumentException("无效的参数"); }
主要区别在于异常类的名称可能略有不同。
1.13.5 自定义异常
在C#和Java中创建自定义异常的方式也很相似,但C#提供了更多的构造函数选项。
C#示例:
public class CustomException : Exception { public CustomException() { } public CustomException(string message) : base(message) { } public CustomException(string message, Exception inner) : base(message, inner) { } protected CustomException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } }
Java示例:
public class CustomException extends Exception { public CustomException() { } public CustomException(String message) { super(message); } public CustomException(String message, Throwable cause) { super(message, cause); } public CustomException(Throwable cause) { super(cause); } }
using var fileStream = new FileStream("path/to/file.txt", FileMode.Open, FileAccess.Read); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { // 处理读取的数据 }
Java示例:
try (FileInputStream fis = new FileInputStream("path/to/file.txt")) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1) { // 处理读取的数据 } }
内存流
C#示例:
using var memoryStream = new MemoryStream(); byte[] data = Encoding.UTF8.GetBytes("Hello, World!"); await memoryStream.WriteAsync(data, 0, data.Length); memoryStream.Position = 0; string result = Encoding.UTF8.GetString(memoryStream.ToArray());
Java示例:
ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] data = "Hello, World!".getBytes(StandardCharsets.UTF_8); baos.write(data); String result = baos.toString(StandardCharsets.UTF_8);