2

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!

Zigrivers
  • 409
  • 7
  • 18
  • Why do you need the constraint at all? The `Characteristic` base class never manipulates `Value` and the subclasses know the concrete type they're dealing with so can perform the operation. – Lee Feb 01 '17 at 17:06
  • It sounds like you might be trying to create a discriminated union; which doesn't exist in C# natively. Here's example of how to create one: http://stackoverflow.com/a/3199453/7495 – John Kraft Feb 01 '17 at 17:41

2 Answers2

3

Limiting a generic class to just int and string is not possible.

  • there is no "just these classes" constraint
  • 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 (i.e. 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). I.e. Is there a constraint that restricts my generic method to numeric types?

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
2

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.

Ðаn
  • 10,934
  • 11
  • 59
  • 95