4人参与 • 2025-07-26 • Java
以下是一个针对 java 开发者快速转向 c# 的简明教程,重点对比 java 与 c# 的异同,帮助你快速上手。
项目结构:
java | c# |
---|---|
int a = 10; | int a = 10; |
string name = "hello"; | string name = "hello"; |
final int max = 100; | const int max = 100; |
var list = new arraylist<>(); (java 10+) | var list = new list<string>(); |
c# 特色:
var
是隐式类型变量(编译器推断类型)。dynamic
类型可动态赋值(类似 object)。c# 的扩展方法允许你为现有类型 (包括密封类、接口、甚至第三方库的类型)“添加”方法,而无需修改其源代码或继承。这是 c# 特有的语法特性,java 中无直接等价物(需通过工具类或继承实现)。
定义扩展方法
// 静态类:扩展方法容器 public static class stringextensions { // 扩展 string 类型的方法 public static bool isnullorempty(this string str) { return string.isnullorempty(str); } }
使用拓展方法
string name = null; // 调用扩展方法(如同实例方法) if (name.isnullorempty()) { console.writeline("name is null or empty"); }
值得注意的是,拓展方法作为一个语法糖对应的可以解决在java中 xxutils 的工具类。同时具有以下注意:
c# 提供了强大的空值处理运算符,简化空值检查逻辑,避免 nullreferenceexception
。
用于安全访问对象的成员,若对象为 null
则返回 null
而非抛出异常。
person person = getperson(); // 可能为 null // 安全访问属性和方法 int length = person?.name?.length ?? 0; person?.sayhello();
对比 java
java 中需显式判断或使用 optional
:
int length = optional.ofnullable(person) .map(p -> p.getname()) .map(string::length) .orelse(0);
提供默认值,当左侧表达式为 null
时返回右侧值。
string name = null; string displayname = name ?? "guest"; // 如果 name 为 null,则使用 "guest"
对比 java
java 中使用三元运算符或 optional
:
string displayname = name != null ? name : "guest"; // 或 string displayname = optional.ofnullable(name).orelse("guest");
仅当变量为 null
时才赋值(c# 8.0+)。
string message = getmessage(); message ??= "default message"; // 如果 getmessage() 返回 null,则赋值为默认值
对比 java
if (message == null) { message = "default message"; }
告知编译器某个表达式不为 null
(c# 8.0+,用于可空引用类型上下文)。
string name = getname()!; // 告诉编译器 getname() 返回值不为 null
null
(需结合 ??
使用)。null
检查,但不适用于值类型(如 int
)。?.
和 ??
组合可显著减少防御性代码(如嵌套 if
判断)。public class person { // 字段 private string name; // 属性(推荐封装字段) public string name { get { return name; } set { name = value; } } // 构造函数 public person(string name) { this.name = name; } // 方法 public void sayhello() { console.writeline($"hello, {name}"); } }
对比 java:
property
(属性)替代 java 的 getter/setter
。this
关键字用法相同。// 继承 public class student : person { public student(string name) : base(name) {} } // 接口 public interface irunnable { void run(); } public class car : irunnable { public void run() { console.writeline("car is running"); } }
对比 java:
:
替代 java 的 extends/implements
。public
,无需显式声明。// 委托(类似 java 的函数式接口) // 定义一个名为 notify 的委托类型,它表示一种方法模板,要求方法返回 void 并接受一个 string 参数 // 类比 java :类似 java 中的函数式接口(如 consumer<string>),但 c# 的委托更直接,可以直接绑定方法。 public delegate void notify(string message); // 事件 public class eventpublisher { // 声明一个事件 onnotify,其类型是 notify 委托。事件本质上是委托的安全封装,外部只能通过 +=/-= 订阅/取消订阅,不能直接调用(如 onnotify.invoke() 会报错)。 public event notify onnotify; // 调用 onnotify?.invoke(...) 触发事件,?. 是空值保护操作符(避免空引用异常)。只有当至少有一个订阅者时,才会执行。 public void triggerevent() { onnotify?.invoke("event triggered!"); } } // 使用 eventpublisher publisher = new eventpublisher(); publisher.onnotify += (msg) => console.writeline(msg); publisher.triggerevent();
订阅事件
使用 +=
运算符将一个 lambda 表达式 (msg) => console.writeline(msg)
绑定到 onnotify 事件。当事件触发时,会执行此方法。
触发事件
调用 triggerevent() 后,所有订阅者都会收到 “event triggered!” 消息。
var numbers = new list<int> { 1, 2, 3, 4, 5 }; var even = numbers.where(n => n % 2 == 0).tolist();
对比 java:
public async task downloaddataasync() { var client = new httpclient(); var data = await client.getstringasync("https://example.com"); console.writeline(data); }
对比 java:
completablefuture
,但语法更直观。parallel.invoke(() => { // 并行执行cpu密集任务 });
java | c# |
---|---|
maven/gradle | nuget(包管理) |
spring | .net core(框架) |
junit | xunit/nunit(测试框架) |
intellij idea | visual studio / intellij rider(ide) |
// 文件:program.cs using system; namespace myapplication; class program { static void main(string[] args) { console.writeline("hello world!"); } }
对比 java:
namespace
组织代码,java 使用 package
。main
方法(java 是 main
)。java | c# |
---|---|
system.out.println() | console.writeline() |
arraylist<t> | list<t> |
hashmap<k,v> | dictionary<k,v> |
interface | interface |
enum | enum |
try-catch-finally | try-catch-finally |
在 c# 中,反射(reflection) 是一种强大的机制,允许在运行时动态地获取类型信息、创建对象实例、调用方法、访问字段和属性等。对于从 java 转向 c# 的开发者来说,反射的概念是相似的,但 c# 的反射 api 更加简洁、直观,并且与语言特性(如 dynamic
、nameof
、linq
)结合更紧密。
反射是指在 运行时(runtime) 动态地:
功能 | java | c# |
---|---|---|
获取类型对象 | myclass.class 或 obj.getclass() | typeof(myclass) 或 obj.gettype() |
获取方法 | clazz.getmethod("name", params...) | type.getmethod("name") |
调用方法 | method.invoke(obj, args) | method.invoke(obj, args) |
获取属性 | clazz.getdeclaredfield("name") | type.getproperty("name") |
获取程序集 | 无直接等价物 | assembly.getexecutingassembly() |
动态创建实例 | clazz.newinstance() | activator.createinstance(type) |
动态访问成员 | 通过 field/method 对象 | 通过 propertyinfo/methodinfo 等 |
// 通过类型名获取 type type = typeof(string); // 通过对象获取 object obj = new person(); type type = obj.gettype();
type type = typeof(person); // 获取所有属性 propertyinfo[] properties = type.getproperties(); // 获取特定方法 methodinfo method = type.getmethod("sayhello"); // 获取所有字段 fieldinfo[] fields = type.getfields();
object person = activator.createinstance(typeof(person));
methodinfo method = type.getmethod("sayhello"); method.invoke(person, null);
propertyinfo prop = type.getproperty("name"); prop.setvalue(person, "alice"); string name = (string)prop.getvalue(person);
fieldinfo field = type.getfield("age", bindingflags.nonpublic | bindingflags.instance); field.setvalue(person, 30); int age = (int)field.getvalue(person);
assembly assembly = assembly.getexecutingassembly(); foreach (type type in assembly.gettypes()) { console.writeline(type.name); }
assembly assembly = assembly.loadfile("path/to/mylibrary.dll"); type type = assembly.gettype("mynamespace.myclass"); object instance = activator.createinstance(type); methodinfo method = type.getmethod("dosomething"); method.invoke(instance, null);
dynamic person = new expandoobject(); person.name = "bob"; person.sayhello = new action(() => console.writeline("hello")); person.sayhello(); // 无需反射即可调用
func<object> factory = expression.lambda<func<object>>( expression.new(typeof(person)) ).compile(); object person = factory();
对于高性能场景(如 orm、序列化框架),可以使用:
system.reflection.emit
:动态生成 il 代码source generator
(c# 9+):编译时生成代码,避免运行时反射getmethod
、getproperty
会增加开销methodinfo
、propertyinfo
)expression
构建委托dynamic
(在合适场景下)system.reflection.dispatchproxy
实现代理system.text.json
、newtonsoft.json
等库已优化的反射机制bindingflags.nonpublic
)java | c# |
---|---|
class.forname("myclass") | type.gettype("mynamespace.myclass") |
clazz.newinstance() | activator.createinstance(type) |
method.invoke(obj, args) | method.invoke(obj, args) |
clazz.getdeclaredmethods() | type.getmethods() |
clazz.getdeclaredfields() | type.getfields() |
clazz.getdeclaredfield("name") | type.getfield("name") |
clazz.getdeclaredmethod("name", params...) | type.getmethod("name", parametertypes) |
clazz.getinterfaces() | type.getinterfaces() |
在 c# 中,nuget 是官方推荐的包管理系统,类似于 java 中的 maven/gradle。它用于管理项目依赖项(如第三方库、框架等)。
nuget 包典型命名规则:[组织名].[功能模块].[平台/框架]
newtonsoft.json
、entityframework
等)newtonsoft.json
)# 安装包 dotnet add package newtonsoft.json # 更新包 dotnet add package newtonsoft.json --version 13.0.1 # 卸载包 dotnet remove package newtonsoft.json
在项目文件中添加 <packagereference>
:
<project sdk="microsoft.net.sdk"> <propertygroup> <targetframework>net6.0</targetframework> </propertygroup> <itemgroup> <packagereference include="newtonsoft.json" version="13.0.1" /> </itemgroup> </project>
默认源是 nuget.org,但也可以配置私有源(如公司内部源):
# 添加私有源 dotnet nuget add source https://mycompany.com/nuget -n mycompany
功能 | java (maven/gradle) | c# (nuget) |
---|---|---|
包管理 | pom.xml / build.gradle | .csproj |
安装包 | mvn install / gradle build | dotnet add package |
私有仓库 | settings.xml / repositories { maven { url "..." } } | dotnet nuget add source |
有时你需要引用本地的 dll 文件(如团队内部开发的库、第三方未提供 nuget 包的库),可以通过以下方式实现。
mylibrary.dll
)<project sdk="microsoft.net.sdk"> <propertygroup> <targetframework>net6.0</targetframework> </propertygroup> <itemgroup> <reference include="mylibrary"> <hintpath>..\libraries\mylibrary.dll</hintpath> </reference> </itemgroup> </project>
在 .csproj
中添加以下配置,确保 dll 被复制到 bin
目录:
<contentwithtargetpath include="..\libraries\mylibrary.dll"> <targetpath>mylibrary.dll</targetpath> <copytooutputdirectory>preservenewest</copytooutputdirectory> </contentwithtargetpath>
bin\debug\net6.0
目录即可ld_library_path
/ dyld_library_path
.csproj
中是否设置了 <copytooutputdirectory>preservenewest</copytooutputdirectory>
fusion log viewer
(fuslogvw.exe
)查看绑定失败日志在 c# 中,[dllimport("xxx.dll")]
是 平台调用(platform invocation services,p/invoke) 的核心特性,用于直接调用 非托管代码(如 windows api、c/c++ 编写的 dll)。这是 java 中没有的特性(java 需要通过 jni 调用本地代码)。
[dllimport]
是 system.runtime.interopservices
命名空间下的特性(attribute),用于声明某个方法的实现来自外部 dll。它允许你在 c# 中直接调用 windows api 或其他非托管函数。
using system.runtime.interopservices;
使用 [dllimport("dll名称")]
特性修饰方法,指定 dll 名称和调用约定(calling convention)。
[dllimport("user32.dll", charset = charset.auto)] public static extern int messagebox(intptr hwnd, string text, string caption, uint type);
messagebox(intptr.zero, "hello from c#", "greeting", 0);
参数 | 说明 |
---|---|
dllname | dll 文件名(如 "user32.dll" ) |
charset | 字符集(charset.ansi 、charset.unicode 、charset.auto ) |
callingconvention | 调用约定(默认为 callingconvention.winapi ,也可指定 thiscall 、stdcall 等) |
entrypoint | 可选,指定 dll 中函数的入口点(当方法名与 dll 函数名不同时使用) |
[dllimport("user32.dll", charset = charset.auto)] public static extern int messagebox(intptr hwnd, string text, string caption, uint type); // 调用 messagebox(intptr.zero, "hello from c#", "greeting", 0);
[dllimport("kernel32.dll")] public static extern uint gettickcount(); // 调用 uint tickcount = gettickcount(); console.writeline($"system uptime: {tickcount} ms");
[dllimport("gdi32.dll", charset = charset.auto)] public static extern intptr createdc(string lpszdriver, string lpszdevice, string lpszoutput, intptr lpinitdata); // 调用 intptr hdc = createdc("display", null, null, intptr.zero);
当调用的函数需要结构体或指针参数时,需使用 ref
、out
或 intptr
,并可能需要使用 structlayout
和 marshalas
来控制内存布局。
[structlayout(layoutkind.sequential)] public struct rect { public int left; public int top; public int right; public int bottom; } [dllimport("user32.dll")] [return: marshalas(unmanagedtype.bool)] public static extern bool getwindowrect(intptr hwnd, out rect lprect); // 使用 rect rect; bool success = getwindowrect(hwnd, out rect); if (success) { console.writeline($"window rect: {rect.left}, {rect.top}, {rect.right}, {rect.bottom}"); }
dllimport
仅适用于 windows 平台(除非使用跨平台兼容的库)。user32.dll
、kernel32.dll
)是 windows 系统库,其他平台无法直接使用。unsafe
代码优化。int
对应 int32
,char*
对应 string
)。marshalas
明确指定封送方式(如 unmanagedtype.lpstr
、unmanagedtype.bstr
)。using system; using system.runtime.interopservices; public static class win32api { [dllimport("user32.dll", charset = charset.auto)] public static extern int messagebox(intptr hwnd, string text, string caption, uint type); [dllimport("kernel32.dll")] public static extern uint gettickcount(); [structlayout(layoutkind.sequential)] public struct rect { public int left; public int top; public int right; public int bottom; } [dllimport("user32.dll")] [return: marshalas(unmanagedtype.bool)] public static extern bool getwindowrect(intptr hwnd, out rect lprect); } // 使用 class program { static void main() { // 调用 messagebox win32api.messagebox(intptr.zero, "hello from c#", "greeting", 0); // 获取系统运行时间 uint tickcount = win32api.gettickcount(); console.writeline($"system uptime: {tickcount} ms"); // 获取窗口位置 win32api.rect rect; bool success = win32api.getwindowrect(new intptr(0x123456), out rect); if (success) { console.writeline($"window rect: {rect.left}, {rect.top}, {rect.right}, {rect.bottom}"); } } }
通过掌握 dllimport
和 p/invoke,你可以在 c# 中直接调用 windows api 或其他非托管函数,实现更底层的系统级操作。结合良好的封装和错误处理,可以显著提升程序的功能性和灵活性。
到此这篇关于java快速转c#的文章就介绍到这了,更多相关java快速转c#内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论