一、访问修饰符
public:公有访问,不受任何限制。
private:私有访问,只限于本类成员访问。
protected:受保护的,只限于本类和子类访问。
internal:内部访问,只限于本项目内访问,其他的不能访问
protected internal:内部保护访问,只限于本项目或是子类访问,其他的不能访问
cs
public class AccessDemo
{
// 公有:所有地方可访问
public string PublicField = "公有的字段";
// 私有:仅本类访问
private string PrivateField = "私有的字段";
// 受保护:本类 + 子类访问
protected string ProtectedField = "受保护的字段";
// 内部:仅本项目访问
internal string InternalField = "内部的字段";
// 内部保护:本项目 + 子类访问
protected internal string ProtectedInternalField = "内部保护的字段";
public void ShowPrivate()
{
// 本类可访问私有字段
Console.WriteLine(PrivateField);
}
}
// 子类
public class AccessChild : AccessDemo
{
public void ShowProtected()
{
// 子类可访问受保护字段
Console.WriteLine(ProtectedField);
// 子类可访问内部保护字段(同项目)
Console.WriteLine(ProtectedInternalField);
}
}
// 调用示例
class Program
{
static void Main()
{
AccessDemo demo = new AccessDemo();
Console.WriteLine(demo.PublicField); // 正常访问
// Console.WriteLine(demo.PrivateField); // 编译报错:私有不可访问
// Console.WriteLine(demo.ProtectedField); // 编译报错:受保护不可访问
Console.WriteLine(demo.InternalField); // 同项目可访问
Console.WriteLine(demo.ProtectedInternalField); // 同项目可访问
}
}
二、枚举
是由一组特定常量构成的一组数据结构,是值类型的一种特殊形式,当需要一个由指定常量集合组成的数据类型时,使用枚举类型。枚举声明可以显式地声明 byte、sbyte、short、ushort、int、uint、long 或 ulong 类型作为对应的基础类型。没有显式地声明基础类型的枚举声明意味着所对应的基础类型是 int,在代码中使用枚举,可以将以前笨拙的代码变得优雅简单,更加直观,方便记忆。
cs
// 定义枚举(显式指定基础类型为int,默认也是int)
public enum OrderStatus : int
{
Pending = 1, // 待支付
Paid = 2, // 已支付
Shipped = 3, // 已发货
Completed = 4 // 已完成
}
// 枚举使用场景:替代魔法值,提升可读性
public class Order
{
public int Id { get; set; }
public OrderStatus Status { get; set; }
public void PrintStatus()
{
// 枚举判断(比数字更直观)
switch (Status)
{
case OrderStatus.Pending:
Console.WriteLine("订单待支付");
break;
case OrderStatus.Paid:
Console.WriteLine("订单已支付");
break;
// 其他case...
}
}
}
// 调用示例
Order order = new Order { Id = 1001, Status = OrderStatus.Paid };
order.PrintStatus(); // 输出:订单已支付
Console.WriteLine((int)order.Status); // 转基础类型:2
枚举在什么地方适用呢?
一条普遍规则是,任何使用常量的地方,例如目前用 switch 代码切换常量的地方。如果只有单独一个值(例如,鞋的最大尺寸,或者笼子中能装猴子的最大数目),则还是把这个任务留给常量吧。但是,如果定义了一组值,而这些值中的任何一个都可以用于特定的数据类型,那么将枚举用在这个地方最适合不过。
三、WebAPI 和 WebService的区别:
(https://blog.csdn.net/cysong168/article/details/51433986)webapi使用的是http协议,而webservices采用的是soap协议webapi是无状态的,相对于webserives更轻量级。webapi支持get,和post等http请求
| 维度 | WebService(ASMX) | WebAPI(ASP.NET Core/Web API) |
|---|---|---|
| 通信协议 | 基于 SOAP(XML 封装,HTTP 仅作传输载体) | 原生 HTTP 协议(支持 GET/POST/PUT/DELETE 等) |
| 数据格式 | 强制 XML 格式 | 支持 JSON/XML/FormData 等(默认 JSON) |
| 轻量级 | 重(需 SOAP 头 / 信封封装) | 轻(无额外封装,直接 HTTP 交互) |
| 适用场景 | 传统企业级跨平台(如 Java/.NET 互调) | 移动端 / 前端交互、RESTful 接口 |
| 无状态 | 可配置状态,默认有状态 | 天然无状态(符合 REST 设计) |
C# 实现 WebService(ASMX)
1. 创建 WebService 服务端(.NET Framework)
cs
// 新建 ASP.NET Web 应用程序,添加 WebService 文件(HelloWebService.asmx)
using System.Web.Services;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class HelloWebService : WebService
{
// 标记为 WebMethod,暴露为 SOAP 接口
[WebMethod(Description = "WebService 示例:根据姓名返回问候语")]
public string SayHello(string name)
{
return $"Hello {name} (WebService/SOAP)";
}
// 复杂对象示例
[WebMethod]
public UserInfo GetUserInfo(int userId)
{
// 模拟返回数据
return new UserInfo
{
Id = userId,
Name = "张三",
Age = 25
};
}
}
// 自定义实体(需序列化,WebService 自动处理 XML 序列化)
public class UserInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
2. WebService 客户端调用(C#)
cs
// 方式1:添加服务引用(VS 右键→添加→服务引用,输入 ASMX 地址)
// 方式2:手动生成代理类(svcutil.exe http://localhost:5000/HelloWebService.asmx?wsdl)
class WebServiceClient
{
static void Main(string[] args)
{
// 实例化自动生成的代理类
var client = new HelloWebServiceSoapClient();
// 调用 SOAP 接口(底层会封装 XML 请求)
var result = client.SayHello("李四");
Console.WriteLine(result); // 输出:Hello 李四 (WebService/SOAP)
var user = client.GetUserInfo(1001);
Console.WriteLine($"ID:{user.Id}, Name:{user.Name}, Age:{user.Age}");
client.Close();
}
}
3. WebService 请求格式(SOAP/XML)
XML
<!-- 客户端发送的 SOAP 请求(HTTP 报文体) -->
POST /HelloWebService.asmx HTTP/1.1
Host: localhost:5000
Content-Type: text/xml; charset=utf-8
Content-Length: 320
SOAPAction: "http://tempuri.org/SayHello"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SayHello xmlns="http://tempuri.org/">
<name>李四</name>
</SayHello>
</soap:Body>
</soap:Envelope>
四、C# 实现 WebAPI(ASP.NET Core)
1. 创建 WebAPI 服务端(ASP.NET Core 6+)
cs
// 新建 ASP.NET Core Web API 项目,修改 Program.cs
var builder = WebApplication.CreateBuilder(args);
// 添加控制器支持
builder.Services.AddControllers();
var app = builder.Build();
// 启用路由
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
// 新建控制器 HelloWebApiController.cs
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class HelloWebApiController : ControllerBase
{
// HTTP GET 请求:api/HelloWebApi/SayHello?name=王五
[HttpGet("SayHello")]
public IActionResult SayHello([FromQuery] string name)
{
return Ok($"Hello {name} (WebAPI/HTTP GET)");
}
// HTTP POST 请求:接收 JSON 格式参数
[HttpPost("GetUserInfo")]
public IActionResult GetUserInfo([FromBody] UserRequest request)
{
var user = new UserInfo
{
Id = request.UserId,
Name = "李四",
Age = 30
};
return Ok(user); // 默认返回 JSON 格式
}
}
// 请求/响应实体
public class UserRequest
{
public int UserId { get; set; }
}
public class UserInfo
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
2. WebAPI 客户端调用(C#)
cs
// 使用 HttpClient 调用(轻量级,无代理类依赖)
using System.Net.Http;
using System.Text;
using System.Text.Json;
class WebApiClient
{
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000/");
// 1. 调用 GET 接口
var getResponse = await httpClient.GetAsync("api/HelloWebApi/SayHello?name=王五");
if (getResponse.IsSuccessStatusCode)
{
var getResult = await getResponse.Content.ReadAsStringAsync();
Console.WriteLine(getResult); // 输出:"Hello 王五 (WebAPI/HTTP GET)"
}
// 2. 调用 POST 接口(JSON 参数)
var request = new UserRequest { UserId = 1002 };
var jsonContent = new StringContent(
JsonSerializer.Serialize(request),
Encoding.UTF8,
"application/json"
);
var postResponse = await httpClient.PostAsync("api/HelloWebApi/GetUserInfo", jsonContent);
if (postResponse.IsSuccessStatusCode)
{
var user = await postResponse.Content.ReadFromJsonAsync<UserInfo>();
Console.WriteLine($"ID:{user.Id}, Name:{user.Name}, Age:{user.Age}");
}
}
}
3. WebAPI 请求格式(原生 HTTP/JSON)
cs
# GET 请求
GET /api/HelloWebApi/SayHello?name=王五 HTTP/1.1
Host: localhost:5000
# POST 请求(JSON 报文体)
POST /api/HelloWebApi/GetUserInfo HTTP/1.1
Host: localhost:5000
Content-Type: application/json
Content-Length: 18
{"UserId":1002}
4. 核心差异总结(代码层面)
- 协议封装 :
- WebService 需通过
[WebMethod]标记,请求 / 响应被 SOAP 信封(XML)封装,需解析 SOAP 头; - WebAPI 直接映射 HTTP 方法(
[HttpGet]/[HttpPost]),无额外封装,报文体为 JSON/FormData 等轻量级格式。
- WebService 需通过
- 客户端调用 :
- WebService 需生成代理类(依赖 WSDL),调用方式笨重;
- WebAPI 可通过
HttpClient直接调用,无需代理,适配移动端 / 前端更友好。
- 扩展性 :
- WebService 仅支持 HTTP 传输,数据格式固定为 XML;
- WebAPI 支持 HTTP/HTTPS,可扩展自定义媒体类型(如 Protobuf),符合 RESTful 设计。
- 性能 :
- WebService 因 XML 解析、SOAP 封装,性能损耗大;
- WebAPI 轻量级,JSON 解析效率高,无状态设计更适合高并发场景。
5. 适用场景选择
- 选 WebService:传统企业级系统对接(如 .NET Framework 与 Java 互调)、需严格遵循 WS-* 规范(如事务、安全);
- 选 WebAPI:移动端 / 前端交互、微服务接口、高并发轻量级接口、RESTful 设计架构。
(注:目前 WebService 已逐步被 WebAPI 替代,ASP.NET Core 已不再推荐使用 ASMX,优先选择 WebAPI 开发 HTTP 接口。)
五、http soap关系:
1、HTTP 与 SOAP 关系的核心逻辑(先理清概念)
- HTTP 是「传输协议」:负责建立 TCP 连接、定义请求 / 响应的「骨架」(请求行、响应行、头信息、体信息),但不规定「消息体的格式」------ 体可以是纯文本、JSON、XML,甚至二进制。
- SOAP 是「消息协议」 :依赖 HTTP(或 SMTP/TCP)作为传输载体,强制规定了消息体必须是 XML 格式,且有固定的 XML 结构(Envelope 信封、Header 头、Body 体)。
- 通俗理解:HTTP 像「快递服务」(负责把包裹从 A 送到 B),SOAP 像「包裹的标准化包装格式」(要求包裹必须用特定纸箱、贴特定标签);而普通 HTTP 接口(如 WebAPI)的消息体就是「无包装的快递内容」(纯文本 / JSON)。
2、C# 代码直观对比:HTTP 原生请求 vs SOAP 请求
1. 纯 HTTP 原生请求(无 SOAP 封装,文本 / JSON 格式)
模拟客户端直接发送 HTTP 请求,消息体为「纯文本 / JSON」(符合你说的「http 纯文本格式」):
cs
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace HttpRawDemo
{
class Program
{
static async Task Main(string[] args)
{
// 1. 创建 HTTP 客户端(模拟 TCP 连接建立)
using var httpClient = new HttpClient();
var baseUrl = "http://localhost:5000/api/Test";
// 2. 场景1:HTTP GET(纯文本参数,无消息体)
// 请求格式:GET + URL 参数 + HTTP 头
var getResponse = await httpClient.GetAsync($"{baseUrl}/Hello?name=张三");
Console.WriteLine("=== 纯 HTTP GET 请求响应 ===");
Console.WriteLine($"状态行:{getResponse.StatusCode}"); // 对应「HTTP/1.1 200 OK」
Console.WriteLine($"响应体(纯文本):{await getResponse.Content.ReadAsStringAsync()}");
// 3. 场景2:HTTP POST(JSON 格式消息体,纯文本类)
var postData = new { Id = 1, Content = "测试纯HTTP POST" };
var jsonContent = new StringContent(
Newtonsoft.Json.JsonConvert.SerializeObject(postData),
Encoding.UTF8,
"application/json" // 告诉服务端:消息体是 JSON 格式(纯文本的一种)
);
var postResponse = await httpClient.PostAsync($"{baseUrl}/PostData", jsonContent);
Console.WriteLine("\n=== 纯 HTTP POST 请求响应 ===");
Console.WriteLine($"状态行:{postResponse.StatusCode}");
Console.WriteLine($"响应体(JSON):{await postResponse.Content.ReadAsStringAsync()}");
}
}
// 配套服务端(ASP.NET Core WebAPI)
// [ApiController]
// [Route("api/[controller]")]
// public class TestController : ControllerBase
// {
// [HttpGet("Hello")]
// public IActionResult Hello(string name)
// {
// return Ok($"Hello {name}(纯HTTP文本响应)");
// }
// [HttpPost("PostData")]
// public IActionResult PostData([FromBody] dynamic data)
// {
// return Ok(new { Result = "成功", Data = data });
// }
// }
}
关键说明:
- HTTP 请求的核心是「请求行(GET/POST + 路径)+ 头信息(Content-Type)+ 消息体」;
- 消息体是「纯文本」(JSON 本质是结构化纯文本),无额外封装,服务端直接解析文本即可。
2. SOAP 请求(基于 HTTP 传输,XML 格式消息体)
SOAP 本质是「HTTP 传输 + 固定结构的 XML 消息体」,以下是 C# 手动构造 SOAP 请求(还原底层逻辑):
csharp
运行
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace SoapOverHttpDemo
{
class Program
{
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
var soapUrl = "http://localhost:5000/HelloWebService.asmx";
// 1. 构造 SOAP 消息体(固定 XML 结构)
// SOAP 核心:Envelope(信封)包裹 Body(业务数据),是 XML 格式的强制规范
var soapXml = @"
<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
<soap:Body>
<SayHello xmlns=""http://tempuri.org/"">
<name>张三</name>
</SayHello>
</soap:Body>
</soap:Envelope>";
// 2. 发送 HTTP POST 请求(SOAP 依赖 HTTP 传输)
var soapContent = new StringContent(
soapXml,
Encoding.UTF8,
"text/xml" // SOAP 必须指定 Content-Type 为 text/xml
);
// SOAP 特有:必须添加 SOAPAction 头(指定要调用的方法)
soapContent.Headers.Add("SOAPAction", "http://tempuri.org/SayHello");
var response = await httpClient.PostAsync(soapUrl, soapContent);
Console.WriteLine("=== SOAP(HTTP+XML)请求响应 ===");
Console.WriteLine($"状态行:{response.StatusCode}");
Console.WriteLine($"响应体(SOAP XML):{await response.Content.ReadAsStringAsync()}");
}
}
// 配套服务端(ASMX WebService)
// [WebService(Namespace = "http://tempuri.org/")]
// public class HelloWebService : WebService
// {
// [WebMethod]
// public string SayHello(string name)
// {
// return $"Hello {name}(SOAP XML 响应)";
// }
// }
}
关键说明:
- SOAP 没有脱离 HTTP,而是「复用 HTTP 的传输能力」,但强制要求:✅ 消息体必须是符合 SOAP 规范的 XML(Envelope/Body 结构);✅ HTTP 头必须包含
Content-Type: text/xml和SOAPAction(指定方法); - 服务端接收到 HTTP 请求后,先解析 XML 结构,再提取业务数据(区别于纯 HTTP 直接解析文本 / JSON)。
3、核心差异代码解释(对应你提到的「请求包格式不同」)
| 维度 | 纯 HTTP 请求(C# 代码体现) | SOAP 请求(C# 代码体现) |
|---|---|---|
| 消息体格式 | StringContent 传入 JSON / 纯文本(如 "{"Id":1}") |
StringContent 传入固定结构的 XML(Envelope 包裹) |
| HTTP 头要求 | 可选 Content-Type: application/json,无强制头 |
必须 Content-Type: text/xml + SOAPAction 头 |
| 解析逻辑 | 服务端用 [FromBody] 直接解析文本 / JSON |
服务端先解析 XML 提取 SOAP Body,再解析业务参数 |
| 依赖的 HTTP 方法 | 支持 GET/POST/PUT/DELETE 等 | 几乎只使用 POST(因为 XML 消息体体积大) |
4、通俗总结(代码层面)
-
HTTP 是「基础通道」 :C# 中
HttpClient发送的所有请求(无论是否带 SOAP),底层都是先建立 TCP 连接(默认 80/443 端口),发送「请求行 + 头 + 体」,服务端返回「状态行 + 头 + 体」------ 这是 HTTP 协议的核心逻辑,SOAP 完全复用这个通道。 -
SOAP 是「通道里的标准化包裹」:普通 HTTP 请求往通道里塞「裸文本 / JSON」,而 SOAP 要求必须塞「带 XML 包装的包裹」,且包裹有固定格式(Envelope),还得贴「SOAPAction 标签」(HTTP 头),服务端必须先拆包装再取内容。
-
代码层面的核心区别:
- 纯 HTTP:
HttpClient直接发文本 / JSON,服务端直接解析; - SOAP:
HttpClient发 XML 格式的 SOAP 信封,服务端先解析 XML 结构,再提取业务数据。
- 纯 HTTP:
(补充:你提到的「http 纯文本格式」更准确的表述是「HTTP 消息体支持纯文本 / JSON 等无封装格式」,而 SOAP 是「HTTP 传输 + XML 封装的消息格式」,二者并非并列的「底层通信协议」------ HTTP 是传输协议,SOAP 是基于 HTTP 的消息协议。)
六、WCF和WEB API我该选择哪个
1、当你想创建一个支持消息、消息队列、双工通信的服务时,你应该选择WCF
2、当你想创建一个服务,可以用更快速的传输通道时,像TCP、Named Pipes或者甚至是UDP(在WCF4.5中),在其他传输通道不可用的时候也可以支持HTTP
3、当你想创建一个基于HTTP的面向资源的服务并且可以使用HTTP的全部特征时(比如URIs、request/response头,缓存,版本控制,多种内容格式),你应该选择Web API
4、当你想让你的服务用于浏览器、手机、iPhone和平板电脑时,你应该选择Web API
先明确核心选型逻辑:WCF 面向「复杂通信场景」(多协议、双工、队列),Web API 面向「HTTP 轻量级资源交互」(移动端 / 前端、RESTful),以下通过代码实现典型场景,直观体现选型差异。
场景 1:WCF 适用场景(双工通信 / 多传输协议 / 消息队列)
子场景 1.1:WCF 双工通信(客户端 - 服务端双向实时通信)
适用场景:即时聊天、设备状态推送、实时监控(如工业设备向服务端上报状态,服务端主动下发指令)。
步骤 1:定义双工契约(接口)
cs
// 1. 客户端回调接口(服务端主动调用客户端方法)
[ServiceContract]
public interface IClientCallback
{
[OperationContract(IsOneWay = true)] // 单向调用,无需等待响应
void OnServerPush(string message); // 服务端推送给客户端的方法
}
// 2. 服务端契约(继承 CallbackContract)
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface IDuplexChatService
{
[OperationContract]
void JoinChat(string userName); // 客户端加入聊天
[OperationContract(IsOneWay = true)]
void SendMessage(string userName, string message); // 客户端发送消息
}
步骤 2:实现 WCF 服务端
cs
// 服务实现类
public class DuplexChatService : IDuplexChatService
{
// 保存所有连接的客户端回调
private static readonly List<IClientCallback> _clients = new List<IClientCallback>();
public void JoinChat(string userName)
{
// 获取当前客户端的回调接口
var clientCallback = OperationContext.Current.GetCallbackChannel<IClientCallback>();
if (!_clients.Contains(clientCallback))
{
_clients.Add(clientCallback);
// 服务端主动推送:通知所有客户端有人加入
BroadcastMessage("系统", $"{userName} 加入聊天");
}
}
public void SendMessage(string userName, string message)
{
// 广播消息给所有客户端
BroadcastMessage(userName, message);
}
// 服务端主动推送消息给所有客户端(双工核心)
private void BroadcastMessage(string sender, string message)
{
var disconnectedClients = new List<IClientCallback>();
foreach (var client in _clients)
{
try
{
// 调用客户端的 OnServerPush 方法(服务端主动推)
client.OnServerPush($"[{DateTime.Now:HH:mm:ss}] {sender}: {message}");
}
catch (Exception)
{
// 记录断开连接的客户端
disconnectedClients.Add(client);
}
}
// 移除断开的客户端
foreach (var client in disconnectedClients)
{
_clients.Remove(client);
}
}
}
// 启动 WCF 服务端(支持 TCP 传输)
class WcfServer
{
static void Main(string[] args)
{
// 1. 创建 TCP 绑定(比 HTTP 更快,适合内网实时通信)
var tcpBinding = new NetTcpBinding();
tcpBinding.Security.Mode = SecurityMode.None; // 简化配置,生产环境需加密
// 2. 配置服务地址(TCP 端口)
var address = new Uri("net.tcp://localhost:8080/DuplexChatService");
// 3. 启动服务宿主
using (var host = new ServiceHost(typeof(DuplexChatService), address))
{
host.AddServiceEndpoint(typeof(IDuplexChatService), tcpBinding, "");
// 启用元数据交换(可选)
host.Description.Behaviors.Add(new ServiceMetadataBehavior());
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
host.Open();
Console.WriteLine("WCF 双工聊天服务已启动(TCP 传输):net.tcp://localhost:8080/DuplexChatService");
Console.WriteLine("按任意键退出...");
Console.ReadKey();
host.Close();
}
}
}
步骤 3:WCF 客户端(接收服务端主动推送)
cs
// 客户端实现(继承回调接口)
public class ChatClient : IClientCallback
{
// 实现服务端推送的方法(服务端主动调用)
public void OnServerPush(string message)
{
// 实时显示服务端推送的消息(双工核心:服务端→客户端)
Console.WriteLine(message);
}
static void Main(string[] args)
{
// 1. 创建双工通道工厂(指定回调实例)
var callbackInstance = new ChatClient();
var factory = new DuplexChannelFactory<IDuplexChatService>(
new InstanceContext(callbackInstance),
new NetTcpBinding(SecurityMode.None),
new EndpointAddress("net.tcp://localhost:8080/DuplexChatService")
);
// 2. 创建通道并连接服务端
var serviceProxy = factory.CreateChannel();
Console.Write("输入你的昵称:");
var userName = Console.ReadLine();
serviceProxy.JoinChat(userName);
// 3. 循环发送消息(客户端→服务端)
Console.WriteLine("输入消息(输入exit退出):");
string input;
while ((input = Console.ReadLine()) != "exit")
{
serviceProxy.SendMessage(userName, input);
}
// 4. 关闭通道
((ICommunicationObject)serviceProxy).Close();
factory.Close();
}
}
核心优势:
- 支持 TCP 传输(比 HTTP 快,内网通信首选);
- 双工通信:服务端可主动向客户端推送消息(Web API 无法原生实现,需依赖 SignalR 扩展);
- 适用于「实时性要求高、双向通信」的场景(如工业监控、即时聊天)。
子场景 1.2:WCF 多传输协议支持(TCP / 命名管道 / HTTP 切换)
适用场景:内网服务用 TCP / 命名管道(高性能),外网服务降级为 HTTP。
cs
// WCF 服务端配置多端点(同一服务支持不同传输协议)
using (var host = new ServiceHost(typeof(DuplexChatService)))
{
// 端点1:TCP 传输(内网高性能)
host.AddServiceEndpoint(typeof(IDuplexChatService),
new NetTcpBinding(SecurityMode.None),
"net.tcp://localhost:8080/DuplexChatService");
// 端点2:命名管道(本地进程通信,最快)
host.AddServiceEndpoint(typeof(IDuplexChatService),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
"net.pipe://localhost/DuplexChatService");
// 端点3:HTTP 传输(外网兼容)
host.AddServiceEndpoint(typeof(IDuplexChatService),
new BasicHttpBinding(),
"http://localhost:8081/DuplexChatService");
host.Open();
Console.WriteLine("WCF 服务启动(支持 TCP/命名管道/HTTP)");
Console.ReadKey();
}
选型逻辑:Web API 仅支持 HTTP/HTTPS,无法使用 TCP / 命名管道,而 WCF 可灵活切换传输协议,满足不同网络环境的性能需求。
场景 2:Web API 适用场景(HTTP 面向资源 / 移动端 / 多终端兼容)
子场景 2.1:Web API 面向资源的 RESTful 接口(HTTP 全特性)
适用场景:移动端 / 前端调用的用户资源管理(支持 URI、缓存、版本控制、多格式响应)。
步骤 1:创建 Web API 控制器(ASP.NET Core)
cs
[ApiController]
[Route("api/v1/[controller]")] // 版本控制:v1 标识接口版本
[ResponseCache(Duration = 60)] // HTTP 缓存:响应缓存 60 秒
public class UsersController : ControllerBase
{
// 模拟用户数据
private static readonly List<User> _users = new List<User>
{
new User { Id = 1, Name = "张三", Age = 25 },
new User { Id = 2, Name = "李四", Age = 30 }
};
// 1. HTTP GET:根据 ID 获取用户(URI 传参,符合 RESTful)
[HttpGet("{id}")]
[Produces("application/json", "application/xml")] // 支持 JSON/XML 多格式响应
public IActionResult GetUser(int id)
{
var user = _users.FirstOrDefault(u => u.Id == id);
if (user == null)
{
return NotFound(new { Message = "用户不存在" }); // HTTP 404 状态码
}
return Ok(user); // HTTP 200 + JSON/XML 响应
}
// 2. HTTP POST:创建用户(Request Body 传参)
[HttpPost]
[Consumes("application/json")] // 仅接收 JSON 格式
public IActionResult CreateUser([FromBody] User user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // HTTP 400 状态码
}
user.Id = _users.Max(u => u.Id) + 1;
_users.Add(user);
// 返回 HTTP 201 + 资源 URI(符合 HTTP 规范)
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
// 3. HTTP PUT:更新用户(全量更新)
[HttpPut("{id}")]
public IActionResult UpdateUser(int id, [FromBody] User user)
{
var existingUser = _users.FirstOrDefault(u => u.Id == id);
if (existingUser == null) return NotFound();
existingUser.Name = user.Name;
existingUser.Age = user.Age;
return NoContent(); // HTTP 204 无内容
}
// 4. HTTP DELETE:删除用户
[HttpDelete("{id}")]
public IActionResult DeleteUser(int id)
{
var user = _users.FirstOrDefault(u => u.Id == id);
if (user == null) return NotFound();
_users.Remove(user);
return NoContent();
}
}
// 用户实体(支持 JSON/XML 序列化)
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
步骤 2:Web API 客户端(移动端 / 前端 / 桌面端通用)
cs
// 通用 HttpClient 调用(适配手机/平板/浏览器等所有终端)
class WebApiClient
{
static async Task Main(string[] args)
{
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5000/");
// 1. GET 获取用户(URI 传参,HTTP 缓存自动生效)
var getUserResponse = await httpClient.GetAsync("api/v1/Users/1");
if (getUserResponse.IsSuccessStatusCode)
{
// 支持 JSON/XML 切换(根据 Accept 头)
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var user = await getUserResponse.Content.ReadFromJsonAsync<User>();
Console.WriteLine($"GET 用户:ID={user.Id}, 姓名={user.Name}");
}
// 2. POST 创建用户(JSON 格式,适配移动端)
var newUser = new User { Name = "王五", Age = 28 };
var jsonContent = new StringContent(
System.Text.Json.JsonSerializer.Serialize(newUser),
Encoding.UTF8,
"application/json"
);
var createResponse = await httpClient.PostAsync("api/v1/Users", jsonContent);
if (createResponse.StatusCode == HttpStatusCode.Created)
{
Console.WriteLine("POST 创建用户成功,URI:" + createResponse.Headers.Location);
}
}
}
核心优势:
- 充分利用 HTTP 特性:URI 资源定位、状态码(200/201/404)、缓存、版本控制、多格式响应(JSON/XML);
- 轻量级:无需生成代理类,HttpClient 直接调用,适配手机 / 平板 / 浏览器等所有终端;
- 符合 RESTful 设计:面向资源(用户),而非面向方法,前端 / 移动端开发更友好。
子场景 2.2:Web API 适配移动端(轻量级 JSON 响应)
适用场景:iOS/Android 移动端调用接口(JSON 体积小、解析快,适配低带宽)。
cs
// Web API 优化移动端体验:压缩响应、简化 JSON 格式
[ApiController]
[Route("api/mobile/[controller]")]
public class MobileUsersController : ControllerBase
{
// 移动端专属接口:返回精简的用户信息(减少流量)
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetMobileUser(int id)
{
var user = _users.FirstOrDefault(u => u.Id == id);
if (user == null) return NotFound();
// 精简响应字段(移动端无需冗余数据)
var mobileUser = new
{
uid = user.Id,
name = user.Name,
age = user.Age
};
return Ok(mobileUser); // JSON 响应:{"uid":1,"name":"张三","age":25}
}
}
选型逻辑:
- WCF 默认返回 XML(SOAP 封装),JSON 需额外配置,且响应体积大;
- Web API 默认返回 JSON,可精简字段,适配移动端低带宽、低性能设备(如老旧手机 / 平板)。
核心选型总结(代码层面)
| 场景需求 | 选择 WCF | 选择 Web API |
|---|---|---|
| 双向实时通信(服务端主动推送) | 双工契约 + TCP 传输(代码示例 1.1) | 需依赖 SignalR 扩展(非原生支持) |
| 多传输协议(TCP / 命名管道 / UDP) | 多端点配置(代码示例 1.2) | 仅支持 HTTP/HTTPS |
| 消息队列 / 可靠会话 | WCF 内置支持 | 需集成第三方组件(如 RabbitMQ) |
| HTTP 全特性(缓存 / 版本 / URI / 状态码) | 配置复杂,非原生支持 | 原生支持(代码示例 2.1) |
| 移动端 / 前端 / 多终端适配 | 重(SOAP/XML),适配差 | 轻(JSON),适配所有终端(代码示例 2.2) |
| 开发 / 部署成本 | 高(需配置契约 / 绑定 / 宿主) | 低(ASP.NET Core 一键部署) |
最终选型建议
-
选 WCF:
- 需要双工通信、消息队列、可靠会话;
- 内网服务需高性能传输(TCP / 命名管道);
- 传统企业级系统对接(如与 Java 服务通过 SOAP 交互)。
-
选 Web API:
- 面向前端 / 移动端的 HTTP 接口;
- 需利用 HTTP 缓存、版本控制、URI 资源定位;
- 轻量级、高并发的 RESTful 接口(如电商、社交 App 后端)。
(补充:.NET Core 中 WCF 已逐步被「gRPC + SignalR」替代,而 Web API 是 ASP.NET Core 的核心,推荐新项目优先使用 Web API + SignalR(双工)/gRPC(高性能)组合。)
七、hashmap和hashTable的区别
1.hashTable是Dictionary的子类,HashMap是Map接口的一个实现的类
2.Hashtable中的方法是同步的,而HashMap中的方法缺省情况为非同步
3.HashTabe不允许null值,而hashmap允许有null
4.hashtable默认长度是11,增长方式是0ld*2+1,而hashmap则默认是16,而且一定是2的指数
注:C# 中无 Java 的
HashMap,功能等效类是Dictionary<TKey, TValue>;Hashtable是 .NET 早期非泛型哈希表,二者核心差异与 Java 中 Hashtable/HashMap 一致,以下通过 C# 代码验证你提到的 4 个核心区别。
核心区别先明确(C# 语境)
| 特性 | Hashtable(C#) | Dictionary<TKey,TValue>(等效 Java HashMap) |
|---|---|---|
| 继承 / 接口 | 继承 DictionaryBase,实现 IDictionary |
实现 IDictionary<TKey,TValue>(泛型接口) |
| 线程安全 | 方法默认同步(加锁),线程安全但性能低 | 方法非同步,线程不安全但性能高 |
| Null 支持 | Key/Value 均不允许 null(抛异常) | Key 不允许 null,Value 允许 null |
| 默认容量 / 扩容 | 默认初始容量 11,扩容规则:old * 2 + 1 |
默认初始容量 16,扩容规则:old * 2(始终 2 的幂) |
| 泛型支持 | 非泛型,存取需拆箱 / 装箱(性能损耗) | 泛型,无拆箱 / 装箱(高性能) |
代码验证:逐个拆解核心区别
1. 继承 / 接口差异(代码验证)
cs
using System;
using System.Collections;
using System.Collections.Generic;
class InheritanceDemo
{
static void Main()
{
// 1. Hashtable:继承 DictionaryBase,实现非泛型 IDictionary
Hashtable ht = new Hashtable();
Console.WriteLine("Hashtable 基类:" + ht.GetType().BaseType.Name); // 输出:DictionaryBase
Console.WriteLine("Hashtable 是否实现 IDictionary:" + (ht is IDictionary)); // True
// 2. Dictionary:实现泛型 IDictionary<TKey, TValue>,无继承 DictionaryBase
Dictionary<string, int> dict = new Dictionary<string, int>();
Console.WriteLine("Dictionary 是否实现 IDictionary<TKey,TValue>:" + (dict is IDictionary<string, int>)); // True
Console.WriteLine("Dictionary 基类:" + dict.GetType().BaseType.Name); // 输出:Object(直接继承 Object)
}
}
输出结果:
bash
Hashtable 基类:DictionaryBase
Hashtable 是否实现 IDictionary:True
Dictionary 是否实现 IDictionary<TKey,TValue>:True
Dictionary 基类:Object
关键说明 :C# 中 Hashtable 是早期非泛型哈希表,绑定到 DictionaryBase 抽象类;而 Dictionary<TKey,TValue> 是泛型实现,直接实现 IDictionary<TKey,TValue> 接口,设计更轻量、类型安全。
2. 线程安全:同步(Hashtable)vs 非同步(Dictionary)
cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
class ThreadSafetyDemo
{
static Hashtable ht = new Hashtable();
static Dictionary<string, int> dict = new Dictionary<string, int>();
static void Main()
{
// 测试1:多线程操作 Hashtable(同步方法,无异常)
Console.WriteLine("=== 多线程操作 Hashtable ===");
for (int i = 0; i < 5; i++)
{
new Thread(AddToHashtable).Start(i);
}
Thread.Sleep(1000);
Console.WriteLine("Hashtable 元素数:" + ht.Count); // 稳定输出 5
// 测试2:多线程操作 Dictionary(非同步,大概率抛异常)
Console.WriteLine("\n=== 多线程操作 Dictionary ===");
try
{
for (int i = 0; i < 5; i++)
{
new Thread(AddToDictionary).Start(i);
}
Thread.Sleep(1000);
}
catch (Exception ex)
{
Console.WriteLine("Dictionary 多线程异常:" + ex.Message); // 抛出"集合已修改"异常
}
}
static void AddToHashtable(object index)
{
ht.Add($"key{index}", index); // Hashtable.Add 是同步方法,加锁保护
}
static void AddToDictionary(object index)
{
dict.Add($"key{index}", (int)index); // Dictionary.Add 非同步,多线程竞争会报错
}
}
输出结果:
plaintext
=== 多线程操作 Hashtable ===
Hashtable 元素数:5
=== 多线程操作 Dictionary ===
Dictionary 多线程异常:集合已修改;可能无法执行枚举操作。
关键说明:
Hashtable的所有公共方法(Add/Remove/Get)都加了[MethodImpl(MethodImplOptions.Synchronized)]特性,默认线程安全,但加锁导致性能低;Dictionary无同步锁,多线程操作需手动加锁(lock (dict) { ... }),性能更高,是 C# 推荐的非线程安全哈希表。
3. Null 值支持:Hashtable 禁止 vs Dictionary 有限允许
cs
using System;
using System.Collections;
using System.Collections.Generic;
class NullSupportDemo
{
static void Main()
{
// 测试1:Hashtable 插入 Null Key/Value(抛异常)
Hashtable ht = new Hashtable();
try
{
ht.Add(null, 123); // Key 为 null,直接抛 ArgumentNullException
// ht.Add("key1", null); // Value 为 null,同样抛 ArgumentNullException
}
catch (Exception ex)
{
Console.WriteLine("Hashtable 插入 Null 报错:" + ex.Message);
}
// 测试2:Dictionary 插入 Null(Key 禁止,Value 允许)
Dictionary<string, int?> dict = new Dictionary<string, int?>();
try
{
dict.Add(null, 456); // Key 为 null,抛 ArgumentNullException
}
catch (Exception ex)
{
Console.WriteLine("Dictionary 插入 Null Key 报错:" + ex.Message);
}
dict.Add("key2", null); // Value 为 null,正常执行
Console.WriteLine("Dictionary 插入 Null Value 成功:key2 = " + dict["key2"]); // 输出 null
}
}
输出结果:
bash
Hashtable 插入 Null 报错:值不能为空。
参数名: key
Dictionary 插入 Null Key 报错:值不能为空。
参数名: key
Dictionary 插入 Null Value 成功:key2 =
关键说明:
Hashtable完全禁止 Null:无论是 Key 还是 Value,插入 Null 都会抛出ArgumentNullException;Dictionary<TKey,TValue>仅禁止 Null Key(因为哈希计算依赖 Key),但 Value 允许 Null(需 Value 类型为可空类型,如int?/string)。
4. 初始容量与扩容规则:11 (2n+1) vs 16 (2^n)
cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
class CapacityDemo
{
static void Main()
{
// 测试1:Hashtable 初始容量和扩容规则
Hashtable ht = new Hashtable();
Console.WriteLine("=== Hashtable 容量 ===");
PrintCapacity(ht, "初始容量"); // 初始容量 11
// 插入 12 个元素,触发扩容(11*2+1=23)
for (int i = 0; i < 12; i++) ht.Add($"key{i}", i);
PrintCapacity(ht, "插入12个元素后容量"); // 扩容为 23
// 测试2:Dictionary 初始容量和扩容规则
Dictionary<string, int> dict = new Dictionary<string, int>();
Console.WriteLine("\n=== Dictionary 容量 ===");
PrintDictCapacity(dict, "初始容量"); // 初始容量 16
// 插入 17 个元素,触发扩容(16*2=32)
for (int i = 0; i < 17; i++) dict.Add($"key{i}", i);
PrintDictCapacity(dict, "插入17个元素后容量"); // 扩容为 32
}
// 反射获取 Hashtable 私有字段 capacity
static void PrintCapacity(Hashtable ht, string desc)
{
FieldInfo capacityField = typeof(Hashtable).GetField("capacity", BindingFlags.NonPublic | BindingFlags.Instance);
int capacity = (int)capacityField.GetValue(ht);
Console.WriteLine($"{desc}:{capacity}");
}
// 反射获取 Dictionary 私有字段 buckets(长度即容量)
static void PrintDictCapacity(Dictionary<string, int> dict, string desc)
{
FieldInfo bucketsField = typeof(Dictionary<string, int>).GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance);
Array buckets = (Array)bucketsField.GetValue(dict);
int capacity = buckets.Length;
Console.WriteLine($"{desc}:{capacity}");
}
}
输出结果:
plaintext
=== Hashtable 容量 ===
初始容量:11
插入12个元素后容量:23
=== Dictionary 容量 ===
初始容量:16
插入17个元素后容量:32
关键说明:
Hashtable初始容量 11,扩容公式old * 2 + 1(11→23→47→95...),目的是减少哈希冲突(质数容量分布更均匀);Dictionary初始容量 16,扩容公式old * 2(16→32→64→128...),始终是 2 的幂,利用位运算(hash & (capacity-1))替代取模,提升哈希计算效率(C# 优化手段)。
核心区别总结(C# 语境)
| 特性 | Hashtable | Dictionary<TKey,TValue>(等效 Java HashMap) |
|---|---|---|
| 泛型支持 | 非泛型,存取需拆箱 / 装箱 | 泛型,类型安全,无拆箱 / 装箱 |
| 线程安全 | 默认同步(加锁),线程安全但性能低 | 非同步,需手动加锁,性能高 |
| Null 支持 | Key/Value 均禁止 Null | Key 禁止 Null,Value 允许 Null |
| 初始容量 | 11 | 16 |
| 扩容规则 | old * 2 + 1(质数) | old * 2(2 的幂) |
| 性能 | 低(同步 + 拆箱 + 质数计算) | 高(非同步 + 泛型 + 位运算) |
| 推荐场景 | .NET Framework 旧项目兼容 | .NET Core/.NET 5+ 新项目首选 |
选型建议(C# 开发)
-
选 Dictionary<TKey,TValue>:
- 绝大多数业务场景(非线程安全、泛型、高性能);
- 需要支持 Null Value、追求哈希计算效率(2 的幂容量)。
-
选 Hashtable:
- 维护 .NET Framework 老旧项目;
- 无需手动加锁的简单线程安全场景(但推荐用
ConcurrentDictionary替代,性能更高)。
补充:C# 中
ConcurrentDictionary<TKey,TValue>是线程安全的泛型哈希表,性能优于Hashtable,是现代 .NET 线程安全哈希表的首选。