【C# OOP 入门到精通】从基础概念到 MVC 实战(含 SOLID 原则与完整代码)

在 C# 开发中,面向对象编程(OOP)是构建可维护、可扩展系统的核心思想,尤其在 MVC 框架中,OOP 的封装、继承、多态特性贯穿了 Model 层设计、业务逻辑实现的全过程。本文从基础概念入手,结合企业级实战案例,带您彻底掌握 OOP 的核心用法与设计精髓,代码可直接复制到项目中运行。

一、OOP 核心概念:从基础到进阶

OOP 的四大核心概念(类与对象、封装、继承、多态)是构建复杂系统的基石,我们从 "是什么" 到 "怎么用" 逐步拆解,每个知识点均配套 MVC 实战场景解析。

1.1 类与对象:代码世界的 "模板与实例"

类是对现实事物的抽象描述(如 "汽车" 的模板),对象是类的具体实例(如 "张三的红色宝马"),二者是 OOP 的基础构成单元。
基础用法:类的定义与对象创建

csharp 复制代码
using System;

// 类(模板):描述汽车的共同属性和行为
public class Car
{
    // 字段:存储对象状态(私有字段加下划线前缀,符合C#命名规范)
    private string _color;
    private string _brand;
    
    // 静态字段:所有Car对象共享(如"汽车总数"计数器)
    public static int TotalCarCount { get; private set; }

    // 1. 默认构造函数(无参):创建对象时自动调用
    public Car()
    {
        TotalCarCount++; // 每创建一个对象,总数+1
    }

    // 2. 构造函数重载(带参):简化对象初始化
    public Car(string color, string brand) : this() // 调用无参构造函数
    {
        _color = color;
        _brand = brand;
    }

    // 属性:控制字段访问(公开读取,私有修改)
    public string Color => _color;
    public string Brand => _brand;

    // 方法:定义对象行为
    public void Run()
    {
        Console.WriteLine($"{_brand}({_color})以100km/h行驶");
    }
}

// 测试:创建对象(适用框架:.NET Framework 4.5+ / .NET Core 3.1+)
Car car1 = new Car(); // 无参构造初始化
car1 = new Car("黑色", "宝马"); // 带参构造直接赋值

Car car2 = new Car("白色", "奔驰");
Console.WriteLine($"已创建汽车总数:{Car.TotalCarCount}"); // 输出2(静态成员用"类名.成员"调用)
car2.Run(); // 输出:奔驰(白色)以100km/h行驶

进阶细节:静态成员的 MVC 应用场景

静态成员属于类而非对象,适合存储 "类级别的共享数据",在 MVC 中常用于工具类设计:

示例:DateHelper静态工具类(无需创建对象即可调用)

csharp 复制代码
public static class DateHelper
{
    // 静态方法:格式化日期(MVC中View层常用)
    public static string FormatDate(DateTime date)
    {
        return date.ToString("yyyy-MM-dd HH:mm:ss");
    }
}

// MVC的Controller中调用
string createTime = DateHelper.FormatDate(DateTime.Now);

类与对象关系可视化
new Car黑色宝马 new Car白色奔驰 Car类模板 car1对象 car2对象 静态成员TotalCarCount=2

【类与对象小结】

  • 类是 "模板",定义属性和方法;对象是 "实例",通过new关键字创建。
  • 构造函数重载可满足不同初始化需求,MVC 实体类常用带参构造简化赋值。
  • 静态成员适合工具类场景,避免频繁创建对象造成的性能开销。

1.2 封装:"隐藏细节,暴露接口" 的安全艺术

封装是将数据(字段)和操作数据的方法捆绑,限制外部直接访问数据,仅通过公开接口交互,核心是 "数据安全" 与 "逻辑内聚"。
基础用法:私有字段 + 公开接口

csharp 复制代码
// 封装示例:银行账户(防止余额被随意修改)
public class BankAccount
{
    // 只读字段:仅能在构造函数中赋值(如账户所有人不可变更)
    private readonly string _accountOwner;
    // 私有字段:外部无法直接访问
    private decimal _balance;

    // 公开属性:仅允许读取,不允许直接修改
    public string AccountNumber { get; }
    public decimal Balance { get => _balance; private set => _balance = value; }

    // 构造函数:初始化只读字段和初始状态
    public BankAccount(string accountNumber, string owner)
    {
        AccountNumber = accountNumber;
        _accountOwner = owner;
        _balance = 0; // 初始余额为0
    }

    // 公开方法:存款(含业务校验)
    public void Deposit(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("存款金额必须大于0");
        Balance += amount; // 内部通过private set修改余额
    }

    // 公开方法:取款(含异常处理)
    public bool Withdraw(decimal amount)
    {
        if (amount <= 0)
        {
            Console.WriteLine("取款金额无效");
            return false;
        }
        if (amount > _balance)
        {
            Console.WriteLine("余额不足");
            return false;
        }
        Balance -= amount;
        return true;
    }
}

// 测试:封装的安全性
var account = new BankAccount("622202123456789", "张三");
account.Deposit(1000);
bool success = account.Withdraw(300);
if (success) Console.WriteLine($"取款后余额:{account.Balance}"); // 输出700
// account.Balance = 2000; // 错误:Balance的set访问器是private,无法直接修改

MVC 实战:Model 层的数据封装

在 MVC 中,封装是 Model 层的核心设计思想,通过属性校验确保数据合法性:

csharp 复制代码
using System.ComponentModel.DataAnnotations;

// MVC的User实体(封装用户信息与校验逻辑)
public class User
{
    private string _password;

    [Key] // 标记为主键(对应数据库)
    public int Id { get; set; }

    [Required(ErrorMessage = "用户名不能为空")] // 数据校验
    [StringLength(20, MinimumLength = 3, ErrorMessage = "用户名长度3-20字符")]
    public string Username { get; set; }

    // 密码封装:外部只能通过方法设置(加密存储)
    public string PasswordHash => _password;

    // 公开方法:设置密码(含加密逻辑)
    public void SetPassword(string password)
    {
        if (string.IsNullOrEmpty(password) || password.Length < 6)
            throw new ValidationException("密码长度不能少于6位");
        _password = MD5Encrypt(password); // 模拟加密(实际用System.Security.Cryptography)
    }

    private string MD5Encrypt(string input)
    {
        return input.GetHashCode().ToString(); // 简化示例,生产环境用正式加密算法
    }
}

【封装小结】

  • 核心原则:"隐藏实现细节,暴露最小接口",私有字段存数据,公开属性 / 方法控访问。
  • 业务价值:数据校验、安全逻辑(如密码加密)在封装内部实现,避免外部代码混乱。
  • MVC 场景:Model 层通过DataAnnotations和私有字段,确保进入 Controller 的数据合法安全。

1.3 继承:"复用代码,扩展功能" 的高效方式

继承允许创建新类(子类)继承现有类(父类)的属性和方法,实现代码复用,同时可扩展新功能,核心是 "IS-A" 关系(如 "狗是动物")。
基础用法:子类继承与 base 关键字

csharp 复制代码
// 父类(抽象类):动物(共性)
public abstract class Animal
{
    public int Age { get; set; }

    // 父类带参构造函数
    public Animal(int age)
    {
        Age = age;
        Console.WriteLine($"动物({age}岁)初始化");
    }

    // 抽象方法:子类必须实现(共性行为的不同实现)
    public abstract void MakeSound();

    // 普通方法:子类可直接继承(共享功能)
    public void Breathe()
    {
        Console.WriteLine("用肺呼吸");
    }
}

// 子类:狗(继承Animal,扩展个性)
public class Dog : Animal
{
    public string Nickname { get; set; }

    // 子类构造函数:用base调用父类构造
    public Dog(string nickname, int age) : base(age)
    {
        Nickname = nickname;
        Console.WriteLine($"狗({nickname},{age}岁)初始化");
    }

    // 重写抽象方法:实现狗的叫声
    public override void MakeSound()
    {
        Console.WriteLine($"{Nickname}:汪汪叫");
    }

    // 子类新增方法:扩展父类没有的功能
    public void GuardHouse()
    {
        Console.WriteLine($"{Nickname}正在看家");
    }
}

// 密封类:禁止被继承(MVC框架常用)
public sealed class SealedDog : Dog
{
    public SealedDog(string nickname, int age) : base(nickname, age) { }
}

// 测试:继承的代码复用
Dog wangcai = new Dog("旺财", 3);
wangcai.Breathe(); // 继承父类方法:用肺呼吸
wangcai.MakeSound(); // 重写方法:旺财:汪汪叫
wangcai.GuardHouse(); // 子类新增方法:旺财正在看家
// public class XiaoWangCai : SealedDog { } // 错误:密封类不能被继承

继承层次可视化
属性 方法 抽象方法 新增方法 Animal抽象类 Dog子类 Cat子类 SealedDog密封子类 Age Breathe MakeSound GuardHouse

MVC 实战:Controller 的继承体系

在 MVC 中,所有控制器都继承自Controller基类,体现继承的代码复用价值:

csharp 复制代码
// MVC的自定义基类(继承自框架Controller)
public class BaseController : Controller
{
    // 所有子类共享的功能:用户登录校验
    protected bool IsUserLogin()
    {
        return Session["UserId"] != null; // 校验Session
    }

    // 权限检查(子类可重写)
    protected virtual bool CheckPermission(string permission)
    {
        var userPermissions = Session["Permissions"] as List<string>;
        return userPermissions?.Contains(permission) ?? false;
    }
}

// 商品控制器(继承自定义基类)
public class ProductController : BaseController
{
    public ActionResult Edit(int id)
    {
        // 复用父类方法:校验登录
        if (!IsUserLogin())
            return RedirectToAction("Login", "Account");

        // 重写父类方法的场景(如果商品编辑需要特殊权限逻辑)
        // if (!CheckPermission("Product.Edit"))
        //     return Content("无权限");

        // 业务逻辑...
        return View();
    }
}

【继承小结】

  • 核心价值:代码复用 + 功能扩展,父类存共性,子类存个性。
  • 关键语法:base关键字调用父类构造 / 方法,override重写父类虚方法。
  • 设计禁忌:避免多层继承(建议不超过 3 层),复杂场景用 "组合优于继承"(如 MVC 中Page类包含Server属性而非继承)。

1.4 多态:"同一行为,不同实现" 的灵活机制

多态允许不同对象对同一消息做出不同响应,通过抽象类或接口实现,核心是 "接口统一,实现各异",是 MVC 中解耦的关键技术。
基础用法:抽象类 vs 接口(新手必懂)

多态有两种实现方式,二者适用场景截然不同,对比表如下:

维度 抽象类(Abstract Class) 接口(Interface)
核心关系 IS-A(是什么,如 "狗是动物") CAN-DO(能做什么,如 "能游泳")
成员类型 可包含字段、属性、具体方法、抽象方法 仅含方法、属性、事件的签名(C# 8.0 后可加默认实现)
继承限制 单继承(子类只能继承一个父类) 多实现(类可实现多个接口)
状态管理 可通过字段维护对象状态 无字段,无法管理状态
版本兼容性 新增非抽象方法不影响子类(兼容性高) 新增成员强制子类实现(兼容性低)
MVC 场景 Controller 基类(共享请求处理逻辑) IActionFilter(定义过滤器行为契约)

代码实战:抽象类与接口的协同使用

csharp 复制代码
// 1. 抽象类:定义"动物"的本质(IS-A关系)
public abstract class Animal
{
    public string Name { get; set; }
    public abstract void MakeSound(); // 动物都会叫(共性行为)
}

// 2. 接口:定义"游泳"的能力(CAN-DO关系)
public interface ISwimable
{
    // 接口方法:仅声明,无实现
    void Swim();
}

// 3. 类:狗(是动物,能游泳)
public class Dog : Animal, ISwimable
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}:汪汪叫");
    }

    public void Swim()
    {
        Console.WriteLine($"{Name}用狗刨式游泳");
    }
}

// 4. 类:鱼(是动物,能游泳)
public class Fish : Animal, ISwimable
{
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}:咕嘟叫");
    }

    public void Swim()
    {
        Console.WriteLine($"{Name}用鱼鳍划水游泳");
    }
}

// 5. 类:人(不是动物,但能游泳)
public class Person : ISwimable
{
    public string Name { get; set; }

    public void Swim()
    {
        Console.WriteLine($"{Name}用自由泳游泳");
    }
}

// 测试:多态的灵活调用
List<Animal> animals = new List<Animal>
{
    new Dog { Name = "旺财" },
    new Fish { Name = "金鱼" }
};

// 同一方法调用,不同对象有不同响应
foreach (var animal in animals)
{
    animal.MakeSound();
}

// 接口的多实现调用
List<ISwimable> swimmers = new List<ISwimable>
{
    new Dog { Name = "旺财" },
    new Fish { Name = "金鱼" },
    new Person { Name = "张三" }
};

foreach (var swimmer in swimmers)
{
    swimmer.Swim();
}

MVC 实战:过滤器的多态应用

MVC 的过滤器通过接口实现多态,不同过滤器有不同实现但接口统一:

csharp 复制代码
// 框架定义的接口(CAN-DO关系)
public interface IActionFilter
{
    void OnActionExecuting(ActionExecutingContext context); // Action执行前
    void OnActionExecuted(ActionExecutedContext context);  // Action执行后
}

// 日志过滤器(实现接口)
public class LogFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine($"请求:{context.HttpContext.Request.Path}");
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine($"响应状态:{context.HttpContext.Response.StatusCode}");
    }
}

// 权限过滤器(实现接口)
public class AuthFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        if (context.HttpContext.Session["UserId"] == null)
        {
            context.Result = new RedirectResult("/Account/Login");
        }
    }

    public void OnActionExecuted(ActionExecutedContext context) { }
}

// Controller中自动调用(框架通过接口统一调度)
public class HomeController : Controller
{
    // 过滤器通过特性绑定
    [ServiceFilter(typeof(LogFilter))]
    [ServiceFilter(typeof(AuthFilter))]
    public IActionResult Index()
    {
        return View();
    }
}

【多态小结】

  • 核心价值:解耦调用者与实现者,调用者只需依赖抽象(接口 / 抽象类),无需关心具体实现。
  • 选型原则:共性大于个性用抽象类(如动物类群),个性大于共性用接口(如游泳能力)。
  • MVC 精髓:通过接口(如IActionFilter)实现插件化扩展,新增功能无需修改原有框架代码。

二、OOP 设计原则:SOLID 原则实战解析

SOLID 原则是 OOP 设计的黄金准则,遵循这些原则可大幅提升代码的可维护性和扩展性,以下结合 C# 与 MVC 场景逐一解析。

2.1 单一职责原则(SRP)

定义: 一个类只负责一项职责,只有一个改变的原因。
反例: UserService同时处理用户管理和订单创建(两个职责)。正例: 拆分为用户服务和订单服务,各司其职:

csharp 复制代码
// 1. 用户服务(仅负责用户相关逻辑)
public class UserService
{
    public void CreateUser(User user)
    {
        // 校验用户数据、保存到数据库...
    }

    public User GetUserById(int id)
    {
        // 查询用户...
        return new User();
    }
}

// 2. 订单服务(仅负责订单相关逻辑)
public class OrderService
{
    public void CreateOrder(Order order)
    {
        // 校验订单、关联用户...
    }
}

// MVC Controller调用(职责清晰)
public class OrderController : Controller
{
    private readonly UserService _userService;
    private readonly OrderService _orderService;

    public OrderController(UserService userService, OrderService orderService)
    {
        _userService = userService;
        _orderService = orderService;
    }

    public IActionResult Create(OrderDto dto)
    {
        var user = _userService.GetUserById(dto.UserId);
        if (user == null) return NotFound();
        
        var order = new Order { UserId = dto.UserId, TotalAmount = dto.Amount };
        _orderService.CreateOrder(order);
        return Ok();
    }
}

2.2 开放封闭原则(OCP)

定义: 对扩展开放,对修改封闭(新增功能通过扩展实现,不修改原有代码)。
实战案例: 订单计算折扣(新增折扣类型无需修改订单类):

csharp 复制代码
// 1. 抽象折扣接口(稳定)
public interface IDiscountStrategy
{
    decimal CalculateDiscount(decimal amount);
}

// 2. 具体折扣实现(可扩展)
public class VipDiscount : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal amount)
    {
        return amount * 0.8m; // VIP 8折
    }
}

public class NewUserDiscount : IDiscountStrategy
{
    public decimal CalculateDiscount(decimal amount)
    {
        return amount > 100 ? amount - 20 : amount; // 新用户满减
    }
}

// 3. 订单类(无需修改)
public class Order
{
    private readonly IDiscountStrategy _discount;

    public Order(IDiscountStrategy discount)
    {
        _discount = discount;
    }

    public decimal GetFinalAmount(decimal amount)
    {
        return _discount.CalculateDiscount(amount); // 依赖抽象
    }
}

// 调用:新增折扣类型只需加新类
var vipOrder = new Order(new VipDiscount());
var newUserOrder = new Order(new NewUserDiscount());

2.3 里氏替换原则(LSP)

定义: 子类可替换父类,且不改变程序的正确性(子类需遵循父类的行为契约)。
反例: 正方形继承矩形(破坏矩形 "宽高独立" 的契约);
正例: 通过接口实现,避免继承滥用:

csharp 复制代码
// 抽象接口(定义行为契约)
public interface IShape
{
    decimal GetArea();
}

// 矩形(符合契约)
public class Rectangle : IShape
{
    public decimal Width { get; set; }
    public decimal Height { get; set; }

    public decimal GetArea()
    {
        return Width * Height;
    }
}

// 正方形(符合契约,独立实现)
public class Square : IShape
{
    public decimal Side { get; set; }

    public decimal GetArea()
    {
        return Side * Side;
    }
}

// 调用:子类可安全替换
List<IShape> shapes = new List<IShape> { new Rectangle(), new Square() };
foreach (var shape in shapes)
{
    Console.WriteLine(shape.GetArea()); // 行为一致,结果正确
}

2.4 接口隔离原则(ISP)

定义: 客户端不应被迫依赖不需要的接口,应将大接口拆分为小接口。
反例: IWorker接口包含Work和Eat方法,机器人无需Eat却必须实现;
正例: 拆分接口,按需实现:

csharp 复制代码
// 拆分后的小接口
public interface IWorkable
{
    void Work();
}

public interface IFeedable
{
    void Eat();
}

// 人类(需工作和吃饭)
public class HumanWorker : IWorkable, IFeedable
{
    public void Work() => Console.WriteLine("人类工作");
    public void Eat() => Console.WriteLine("人类吃饭");
}

// 机器人(只需工作)
public class RobotWorker : IWorkable
{
    public void Work() => Console.WriteLine("机器人工作");
    // 无需实现Eat方法
}

2.5 依赖倒置原则(DIP)

定义: 高层模块不依赖低层模块,二者均依赖抽象;抽象不依赖细节,细节依赖抽象。
MVC 实战: 日志模块解耦(Controller 不依赖具体日志实现):

csharp 复制代码
// 1. 抽象日志接口(高层与低层共同依赖)
public interface ILogger
{
    void Log(string message);
}

// 2. 低层实现(细节依赖抽象)
public class FileLogger : ILogger
{
    public void Log(string message)
    {
        File.AppendAllText("log.txt", message); // 写文件
    }
}

public class DatabaseLogger : ILogger
{
    public void Log(string message)
    {
        // 写数据库...
    }
}

// 3. 高层模块(Controller依赖抽象)
public class UserController : Controller
{
    private readonly ILogger _logger;

    // 构造函数注入(依赖抽象而非具体)
    public UserController(ILogger logger)
    {
        _logger = logger;
    }

    public IActionResult Login()
    {
        _logger.Log("用户登录请求"); // 调用抽象方法
        return View();
    }
}

// 配置依赖(切换实现只需改配置)
services.AddScoped<ILogger, FileLogger>();
// services.AddScoped<ILogger, DatabaseLogger>();

【SOLID 原则小结】

  • SRP:一个类干一件事,避免 "万能类";
  • OCP:通过抽象扩展功能,拒绝 "修改原有代码";
  • LSP:子类不破坏父类契约,确保替换安全;
  • ISP:接口最小化,避免 "被迫实现无用方法";
  • DIP:依赖抽象解耦,实现 "插件化替换"。

三、MVC 实战落地:OOP 综合应用案例

结合上述知识,我们构建一个 MVC 的 "订单管理系统" 核心模块,涵盖实体设计、业务逻辑、控制器调用全流程,代码可直接复制到项目中运行。

3.1 实体层设计(Model)

csharp 复制代码
using System;
using System.ComponentModel.DataAnnotations;

// 1. 基类(继承复用)
public abstract class BaseEntity
{
    [Key]
    public int Id { get; set; }
    public DateTime CreateTime { get; set; } = DateTime.Now;
}

// 2. 用户实体(封装)
public class User : BaseEntity
{
    private string _password;

    [Required]
    [StringLength(20, MinimumLength = 3)]
    public string Username { get; set; }

    public string Email { get; set; }
    public string PasswordHash => _password;

    public void SetPassword(string password)
    {
        if (password.Length < 6)
            throw new ValidationException("密码不少于6位");
        _password = BCrypt.Net.BCrypt.HashPassword(password); // 正式加密
    }

    public bool VerifyPassword(string password)
    {
        return BCrypt.Net.BCrypt.Verify(password, _password);
    }
}

// 3. 订单实体(多态)
public class Order : BaseEntity
{
    public int UserId { get; set; }
    public decimal TotalAmount { get; set; }
    public string Status { get; set; } = "待支付";

    // 虚方法:状态变更(允许子类重写)
    public virtual void ChangeStatus(string newStatus)
    {
        Status = newStatus;
        Console.WriteLine($"订单{Id}状态变更为:{newStatus}");
    }
}

// 4. 退款订单(继承+多态)
public class RefundOrder : Order
{
    public decimal RefundAmount { get; set; }
    public string RefundReason { get; set; }

    // 重写父类方法:添加退款特有逻辑
    public override void ChangeStatus(string newStatus)
    {
        base.ChangeStatus(newStatus); // 调用父类逻辑
        if (newStatus == "已退款")
        {
            Console.WriteLine($"订单{Id}已退款{RefundAmount}元,通知财务处理");
        }
    }
}

3.2 业务逻辑层(Service)

csharp 复制代码
using System.Linq;
using Microsoft.EntityFrameworkCore;

// 1. 抽象服务接口(依赖倒置)
public interface IOrderService
{
    Order CreateOrder(int userId, decimal amount);
    bool ChangeOrderStatus(int orderId, string status);
}

// 2. 具体服务实现
public class OrderService : IOrderService
{
    private readonly AppDbContext _dbContext;
    private readonly ILogger _logger;

    // 构造函数注入依赖
    public OrderService(AppDbContext dbContext, ILogger logger)
    {
        _dbContext = dbContext;
        _logger = logger;
    }

    public Order CreateOrder(int userId, decimal amount)
    {
        // 校验用户存在
        var user = _dbContext.Users.Find(userId);
        if (user == null)
            throw new KeyNotFoundException("用户不存在");

        // 创建订单
        var order = new Order
        {
            UserId = userId,
            TotalAmount = amount
        };

        _dbContext.Orders.Add(order);
        _dbContext.SaveChanges();

        _logger.Log($"创建订单:{order.Id},用户:{userId}");
        return order;
    }

    public bool ChangeOrderStatus(int orderId, string status)
    {
        var order = _dbContext.Orders
            .Include(o => o as RefundOrder) // 加载子类数据
            .FirstOrDefault(o => o.Id == orderId);

        if (order == null)
            return false;

        // 多态调用:自动执行对应子类的逻辑
        order.ChangeStatus(status);
        _dbContext.SaveChanges();
        return true;
    }
}

3.3 控制器层(Controller)

csharp 复制代码
using Microsoft.AspNetCore.Mvc;

public class OrderController : BaseController
{
    private readonly IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService;
    }

    // 创建订单(POST请求)
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Create(CreateOrderDto dto)
    {
        // 校验登录(复用BaseController方法)
        if (!IsUserLogin())
            return RedirectToAction("Login", "Account");

        try
        {
            var order = _orderService.CreateOrder(GetCurrentUserId(), dto.Amount);
            return Json(new { success = true, orderId = order.Id });
        }
        catch (Exception ex)
        {
            return Json(new { success = false, message = ex.Message });
        }
    }

    // 变更订单状态
    [HttpPost]
    public IActionResult ChangeStatus(int orderId, string status)
    {
        if (!CheckPermission("Order.Manage"))
            return Forbid();

        var success = _orderService.ChangeOrderStatus(orderId, status);
        return Json(new { success });
    }

    // 获取当前登录用户ID(BaseController方法)
    private int GetCurrentUserId()
    {
        return Convert.ToInt32(Session["UserId"]);
    }
}

3.4 数据库上下文(DbContext)

csharp 复制代码
using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    // 实体集合
    public DbSet<User> Users { get; set; }
    public DbSet<Order> Orders { get; set; }

    // 配置继承关系(EF Core支持)
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 订单的继承配置(TPH模式)
        modelBuilder.Entity<Order>()
            .HasDiscriminator<string>("OrderType")
            .HasValue<Order>("Normal")
            .HasValue<RefundOrder>("Refund");
    }
}

四、常见问题 FAQ(新手避坑指南)

常见问题 解答与解决方案
抽象类能不能直接 new 实例? 不能!抽象类是 "不完整的模板",必须通过子类继承并实现抽象方法后,new 子类对象(如Animal a = new Dog())。
接口里能不能定义字段? 不能!接口只能定义方法、属性、事件的签名,需通过实现类的字段维护状态(如ISwimable的实现类用字段存游泳速度)。
继承和组合选哪个更好? 简单共性用继承(如 Controller 继承),复杂关系用组合(如Order包含User属性而非继承User),遵循 "组合优于继承" 原则。
多态调用时如何获取子类属性? 用as或is转换类型(如var refundOrder = order as RefundOrder; if(refundOrder != null) { ... })。
接口变更后如何兼容旧代码? 新增接口而非修改原有接口(如ISwimableV2继承ISwimable),避免破坏所有实现类。

五、互动时间(3 重福利助力进阶)

本文整合了 OOP 基础、SOLID 原则、MVC 实战三大模块,代码已在.NET 6 环境下测试通过,可直接应用于实际项目。
1.基础巩固: 点赞 + 收藏本文,评论区留言 "OOP + 你最想练的案例"(如 "OOP + 购物车模块"),我会回复你的代码设计思路;
2.进阶资料: 评论区留言 "求 OOP 手册",免费送《C# OOP 实战手册》(含 15 个 MVC 模块案例 + SOLID 原则检查表);
3.问题解答: 如果你在 "抽象类 vs 接口选型"" 多态调试 " 上卡壳,评论区描述你的问题,我会置顶详细解答(优先回复点赞前 3 的评论)。

下一篇预告

下一篇我们用 OOP + 设计模式,拆解 "MVC 中的依赖注入(DI)实战"------ 比如如何通过接口解耦 Service 与 Controller,如何设计可测试的业务逻辑,关注我的专栏,不错过核心技术干货!

相关推荐
少许极端4 小时前
算法奇妙屋(七)-字符串操作
java·开发语言·数据结构·算法·字符串操作
懒羊羊不懒@4 小时前
Java基础语法—字面量、变量详解、存储数据原理
java·开发语言
小龙报4 小时前
《算法通关指南---C++编程篇(2)》
c语言·开发语言·数据结构·c++·程序人生·算法·学习方法
古一|4 小时前
Vue3中ref与reactive实战指南:使用场景与代码示例
开发语言·javascript·ecmascript
宠友信息4 小时前
java微服务驱动的社区平台:友猫社区的功能模块与实现逻辑
java·开发语言·微服务
驰羽5 小时前
[GO]golang接口入门:从一个简单示例看懂接口的多态与实现
开发语言·后端·golang
ii_best5 小时前
IOS/ 安卓开发工具按键精灵Sys.GetAppList 函数使用指南:轻松获取设备已安装 APP 列表
android·开发语言·ios·编辑器
王夏奇5 小时前
C++友元函数和友元类!
开发语言·c++
Full Stack Developme5 小时前
jdk.random 包详解
java·开发语言·python