C#多数据类型处理类实现方案技术咨询
Great question! Let’s break down the issues with your current code and walk through more idiomatic, type-safe C# solutions.
Problems with Your Current Implementation
Your approach uses new to shadow the base class’s value field, which creates several pitfalls:
- Broken polymorphism: If you have a
Paramreference pointing to aParam_Intinstance, accessingvaluewill return the base class’sobjectfield instead of the derivedintvalue. Example:Param myParam = new Param_Int("Age"); Console.WriteLine(myParam.value); // Returns the base object, not the int 1 - No type safety: Retrieving values requires unsafe casting, which can throw runtime exceptions if done incorrectly.
- Public fields: Exposing public fields violates encapsulation best practices—properties are preferred for control over access and validation.
Solution 1: Generic Base Class (Recommended)
This approach gives you full type safety while maintaining polymorphism for collections of parameters.
// Optional non-generic base to group all parameter types public abstract class ParamBase { public string Name { get; } protected ParamBase(string name) => Name = name; // Optional: Get untyped value if needed for serialization/debugging public abstract object GetValue(); } // Generic abstract class for typed parameters public abstract class Param<T> : ParamBase { public T Value { get; set; } protected Param(string name, T initialValue) : base(name) { Value = initialValue; } public override object GetValue() => Value; } // Concrete parameter types public class IntParam : Param<int> { public IntParam(string name) : base(name, 1) { } } public class DoubleParam : Param<double> { public DoubleParam(string name) : base(name, 1.0) { } } public class StringParam : Param<string> { public StringParam(string name) : base(name, string.Empty) { } }
Benefits:
- Type safety: No casting required when accessing
Value—you know exactly what type each parameter holds. - Polymorphism: Use
ParamBaseto store all parameter types in a single collection, while still being able to retrieve typed values via pattern matching:var paramsList = new List<ParamBase> { new IntParam("Age"), new DoubleParam("Height"), new StringParam("Name") }; foreach (var param in paramsList) { switch (param) { case IntParam intParam: Console.WriteLine($"Int: {intParam.Name} = {intParam.Value}"); break; case DoubleParam doubleParam: Console.WriteLine($"Double: {doubleParam.Name} = {doubleParam.Value}"); break; } } - Encapsulation: Properties let you add validation logic later (e.g., restricting
IntParamvalues to positive numbers) without breaking existing code.
Solution 2: Simplified Generic Class (No Derived Types)
If you don’t need type-specific behavior (like custom initial values or validation), you can skip the derived classes entirely:
public class Param<T> { public string Name { get; } public T Value { get; set; } public Param(string name, T initialValue) { Name = name; Value = initialValue; } } // Usage var ageParam = new Param<int>("Age", 1); var heightParam = new Param<double>("Height", 1.0); var nameParam = new Param<string>("Name", "");
This is the most concise option for straightforward parameter storage.
What to Avoid
Stick to the generic approaches above instead of shadowing fields with new. If you absolutely need a non-generic base without generics, you could use virtual properties—but this brings back casting risks:
public abstract class Param { public string Name { get; } public abstract object Value { get; set; } protected Param(string name) => Name = name; } public class IntParam : Param { private int _value; public override object Value { get => _value; set => _value = (int)value; // Risk of InvalidCastException } public IntParam(string name) : base(name) => _value = 1; }
This is not recommended unless you have strict legacy constraints.
内容的提问来源于stack exchange,提问作者thefloe

