I'm learning C# currently and have a situation where I would like to have a base abstract class that other classes will inherit from.
My challenge is that I would like to pass in either an integer or a string value depending on the situation.
Currently, I can do that with a generic IF I don't constrain the generic. However, I think that might be a bad practice to not constrain a generic? And if it is a bad practice, how would I constrain the generic so that I'm only taking an integer or string.
Here's an example of what I'm trying to do:
/*
I want to have a base abstract class that can handle
both an integer value and a string value
*/
public abstract class Characteristic<T> where T : int, string {
private T _value;
public T Value {
get { return _value;}
set { _value = value;}
}
}
public class NumericAttribute : Characteristic<int> {
private int _modifiedValue = Value + 1;
}
public class StringAttribute : Characteristic<string> {
private string _modifiedValue = Value + " more text.";
}
Thanks for your help!
Limiting a generic class to just int
and string
is not possible.
int
and string
do not share any common base class or interface that are unique to them int
is a struct
and string
is a class
- so even narrowing choices by struct
/ class
constraint is not possible. So basically generic without type constraints (with possibly checking types at run-time if it is really required) is the best generic type you can get with those 2 types. There is nothing particularly wrong with generics that don't constraint they type arguments (ie List<T>
).
If you want to narrow types at least a bit int
and string
do have some shared interfaces - IComparable
, IConvertible
(non-generic once), but these interfaces are implemented by all numeric types for example.
Note: there are similar questions trying to limit generics to "numerical types" that may give some alternative approaches (including code generation). Ie Is there a constraint that restricts my generic method to numeric types?
It is possible to more-or-less do what you're asking; but as others have already indicated, you might not want to do that (and might not even need the constraint). However, as I indicated in a comment, you make an interface
public interface IModifiable<T>
{
T Modify(T value);
}
and then your own wrapper classes for int
and string
which implement this interface:
public struct Int32 : IModifiable<Int32>
{
public System.Int32 Value { get; set; }
public Int32 Modify(Int32 value)
{
return new Int32() { Value = Value + value.Value };
}
}
public class String : IModifiable<String>
{
public System.String Value { get; set; }
public String Modify(String value)
{
return new String() { Value = Value + value.Value };
}
}
Your base class now has a constraint of your interface
public abstract class Characteristic<T> where T : IModifiable<T>
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; }
}
}
and your derived classes, pretty much as before
public class NumericAttribute : Characteristic<Int32>
{
void f()
{
var _modifiedValue = Value.Modify(new Int32() { Value = 1 });
}
}
public class StringAttribute : Characteristic<String>
{
void f()
{
var _modifiedValue = Value.Modify(new String() { Value = " more text." });
}
}
Again, while this gives you a "solution" to your specific question, you might consider the wisdom of this approach.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.