.NET、C#基础知识学习(1)

一、访问修饰符

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. 核心差异总结(代码层面)
  1. 协议封装
    • WebService 需通过 [WebMethod] 标记,请求 / 响应被 SOAP 信封(XML)封装,需解析 SOAP 头;
    • WebAPI 直接映射 HTTP 方法([HttpGet]/[HttpPost]),无额外封装,报文体为 JSON/FormData 等轻量级格式。
  2. 客户端调用
    • WebService 需生成代理类(依赖 WSDL),调用方式笨重;
    • WebAPI 可通过 HttpClient 直接调用,无需代理,适配移动端 / 前端更友好。
  3. 扩展性
    • WebService 仅支持 HTTP 传输,数据格式固定为 XML;
    • WebAPI 支持 HTTP/HTTPS,可扩展自定义媒体类型(如 Protobuf),符合 RESTful 设计。
  4. 性能
    • 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 关系的核心逻辑(先理清概念)

  1. HTTP 是「传输协议」:负责建立 TCP 连接、定义请求 / 响应的「骨架」(请求行、响应行、头信息、体信息),但不规定「消息体的格式」------ 体可以是纯文本、JSON、XML,甚至二进制。
  2. SOAP 是「消息协议」 :依赖 HTTP(或 SMTP/TCP)作为传输载体,强制规定了消息体必须是 XML 格式,且有固定的 XML 结构(Envelope 信封、Header 头、Body 体)。
  3. 通俗理解: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/xmlSOAPAction(指定方法);
  • 服务端接收到 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、通俗总结(代码层面)

  1. HTTP 是「基础通道」 :C# 中 HttpClient 发送的所有请求(无论是否带 SOAP),底层都是先建立 TCP 连接(默认 80/443 端口),发送「请求行 + 头 + 体」,服务端返回「状态行 + 头 + 体」------ 这是 HTTP 协议的核心逻辑,SOAP 完全复用这个通道。

  2. SOAP 是「通道里的标准化包裹」:普通 HTTP 请求往通道里塞「裸文本 / JSON」,而 SOAP 要求必须塞「带 XML 包装的包裹」,且包裹有固定格式(Envelope),还得贴「SOAPAction 标签」(HTTP 头),服务端必须先拆包装再取内容。

  3. 代码层面的核心区别:

    • 纯 HTTP:HttpClient 直接发文本 / JSON,服务端直接解析;
    • SOAP:HttpClient 发 XML 格式的 SOAP 信封,服务端先解析 XML 结构,再提取业务数据。

(补充:你提到的「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 一键部署)

最终选型建议

  1. 选 WCF

    • 需要双工通信、消息队列、可靠会话;
    • 内网服务需高性能传输(TCP / 命名管道);
    • 传统企业级系统对接(如与 Java 服务通过 SOAP 交互)。
  2. 选 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# 开发)

  1. 选 Dictionary<TKey,TValue>

    • 绝大多数业务场景(非线程安全、泛型、高性能);
    • 需要支持 Null Value、追求哈希计算效率(2 的幂容量)。
  2. 选 Hashtable

    • 维护 .NET Framework 老旧项目;
    • 无需手动加锁的简单线程安全场景(但推荐用 ConcurrentDictionary 替代,性能更高)。

补充:C# 中 ConcurrentDictionary<TKey,TValue> 是线程安全的泛型哈希表,性能优于 Hashtable,是现代 .NET 线程安全哈希表的首选。

相关推荐
leiming62 小时前
c++ string 容器
开发语言·c++·算法
自由与自然2 小时前
栅格布局常用用法
开发语言·前端·javascript
_codemonster3 小时前
python易混淆知识点(十六)lambda表达式
开发语言·python
小梁努力敲代码3 小时前
Java多线程--单例模式
java·开发语言
Percep_gan3 小时前
解决-bash: /usr/bin/yum: No such file or directory
开发语言·bash
缺点内向3 小时前
如何在 C# 中将 Word 文档转换为 EMF(增强型图元文件)
开发语言·c#·word·.net
老华带你飞3 小时前
学生宿舍管理|基于java + vue学生宿舍管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
Filotimo_3 小时前
在java后端开发中,redis的用处
java·开发语言·redis
superman超哥3 小时前
仓颉Option类型的空安全处理深度解析
c语言·开发语言·c++·python·仓颉