C#泛型问题:抽象方法中如何确定使用对应派生类
Let's tackle this problem step by step, starting with fixing the error and then optimizing to reduce duplicate code, followed by addressing your question about generic best practices.
First: Fix the "Type must be a reference type" Error
The root issue is mismatched generic constraints. Your third-party doProcess<T>() requires T to be a reference type (class) with a parameterless constructor (new()), but your getValuesFromFile<T>() only enforces T : FileType.
To resolve this, merge the required constraints into your base class method. Make sure all your FileType implementations (like FileType1, FileType2) are reference types (which they already are, since they're classes) and have a parameterless constructor (add one if they don't—this is mandatory for the third-party library):
abstract class FileProcessor { protected List<T> getValuesFromFile<T>() where T : FileType, class, new() { // Combine all required constraints try { return otherClass.doProcess<T>(); } catch (Exception ex) { throw new Exception("Unable to retrieve the data from the file.", ex); } } }
Note: Constraint order matters here—base class/interface constraints come first, then class/struct, then new().
Second: Minimize Duplicate Code in Derived Processors
To avoid repeating generic method calls in every derived processor, you have two clean options:
Option 1: Make FileProcessor a Generic Base Class
Bind the base class directly to a specific FileType so derived classes don't need to redefine the generic logic:
// Generic base class with all shared logic abstract class FileProcessor<T> where T : FileType, class, new() { protected List<T> GetValuesFromFile() { try { return otherClass.doProcess<T>(); } catch (Exception ex) { throw new Exception("Unable to retrieve the data from the file.", ex); } } } // Derived classes are now clean and type-bound class Processor_FileType1 : FileProcessor<FileType1> {} class Processor_FileType2 : FileProcessor<FileType2> {}
This is the most concise approach—each processor automatically inherits the correct type-specific logic without any redundant code.
Option 2: Keep Non-Generic Base Class, Add Simple Wrappers
If you need a non-generic base class (e.g., for grouping processors in a non-generic collection), add type-specific wrapper methods in derived classes that call the shared generic base method:
abstract class FileProcessor { protected List<T> getValuesFromFile<T>() where T : FileType, class, new() { try { return otherClass.doProcess<T>(); } catch (Exception ex) { throw new Exception("Unable to retrieve the data from the file.", ex); } } } class Processor_FileType1 : FileProcessor { public List<FileType1> GetValues() { return getValuesFromFile<FileType1>(); } } class Processor_FileType2 : FileProcessor { public List<FileType2> GetValues() { return getValuesFromFile<FileType2>(); } }
This still centralizes all error handling and third-party calls in the base class, leaving derived classes with minimal boilerplate.
Is This Bad Generic Programming?
No—this is actually a great use of generics!
Your setup leverages generics to:
- Enforce type safety: Each processor is tied to exactly one
FileType, preventing mismatched file-data pairs. - Reduce code duplication: Shared logic (error handling, third-party calls) lives in the base class instead of being copied across every processor.
- Maintain flexibility: If you add a new
FileType3, you only need to create aProcessor_FileType3class—no need to rewrite the data retrieval logic.
The only caveat is ensuring all FileType implementations meet the class, new() constraints required by the third-party library. As long as you control those classes (which you do, per your code), this is a solid, maintainable design.
内容的提问来源于stack exchange,提问作者John Bustos

