C#中普通类与接口的区别及实例赋值的差异与最佳实践
Great questions—let's break these down clearly, since getting a handle on class vs interface differences and reference type behavior is super important in C#.
1. Differences between a regular class and interface in C#
Here are the core distinctions that matter most in practice:
- Design Intent: Classes are for concrete implementations—they hold state (like fields) and define how things work (methods with actual code). Interfaces are pure contracts—they only say "what a thing can do" via method/property signatures, no implementation or state allowed. Think of interfaces as a job description, and classes as the worker who does the job.
- Inheritance Rules: A class can inherit from one base class (C# enforces single inheritance for classes), but can implement multiple interfaces. Interfaces, though, can inherit from multiple other interfaces—so you can build composite contracts.
- Implementation Obligations: When a class implements an interface, it must code every member defined in the interface (unless it's an abstract class, which can leave some unimplemented). For class inheritance, you only override members if you need to change their behavior.
- Access Modifiers: Interface members are implicitly
public—you can't mark them asprivateorprotected. Class members can use any access modifier to control visibility. - State & Constructors: Classes can have fields, constructors, destructors, and static members. Interfaces can't have any of these—no state storage, no way to initialize an interface directly.
- Use Cases: Use classes when you need to create reusable, tangible objects with specific behavior and state. Use interfaces to define common rules across unrelated classes, enabling polymorphism and making your code easier to swap out or test.
2. Assigning an AuthenticationRepository instance to a class vs interface reference
First, let's set up some example code to make this concrete:
// Interface contract public interface IAuthenticationRepository { bool ValidateUser(string username, string password); } // Concrete class implementing the interface public class AuthenticationRepository : IAuthenticationRepository { // Implemented interface method public bool ValidateUser(string username, string password) { // Actual validation logic here return username == "admin" && password == "secure123"; } // Class-specific method NOT in the interface public void LogLoginAttempt(string username) { Console.WriteLine($"Login attempt for {username}"); } } // Creating instances var classRef = new AuthenticationRepository(); IAuthenticationRepository interfaceRef = new AuthenticationRepository();
What's the visible difference?
- Accessible Members:
classReflets you call all members ofAuthenticationRepository—both the interface'sValidateUserand the class-specificLogLoginAttempt.interfaceRefonly exposes members defined inIAuthenticationRepository—you can't callLogLoginAttemptdirectly (you'd have to cast it back toAuthenticationRepositoryfirst, which is risky if the instance isn't actually that type).
- Polymorphism: The interface reference lets you treat any implementation of
IAuthenticationRepositorythe same way. For example, if you later build aMockAuthenticationRepositoryfor testing, you can swap it intointerfaceRefwithout changing any code that uses the interface.
Is there an essential under-the-hood difference?
Absolutely! Even though both references point to the exact same object in memory, the compiler enforces a "view" of that object based on the reference type. The interface reference restricts you to only the contract defined by IAuthenticationRepository, while the class reference gives you full access to the concrete implementation.
At runtime, the object is still an AuthenticationRepository—the reference type just controls what you can access at compile time, preventing accidental use of class-specific logic when you should be sticking to the agreed-upon contract.
Best Practice
Prefer interface references over concrete class references whenever possible—this is a core part of writing loosely coupled, maintainable code:
- Flexibility: You can swap out the concrete implementation (e.g., switch from a SQL-based repo to a NoSQL one) without touching code that depends on the interface.
- Testability: Mocking interfaces is trivial for unit tests—you don't have to deal with the full concrete class's dependencies (like database connections).
- Clearer Boundaries: Using interfaces makes it explicit which members are part of the public contract consumers should rely on, instead of exposing all internal details of the class.
Only use a concrete class reference when you need access to class-specific members that aren't part of any interface contract.
内容的提问来源于stack exchange,提问作者Ramesh Rajendran




