Tyler Pantuso
Tyler Pantuso

Reputation: 853

Is there a way to extend a built-in type to inherit an interface?

I want to add an interface to some built-in types. I have an interface, IConcludable, which I am using as a constraint for Conclusion<T>. I have no clue how to approach this, or if it is even possible.

Basic Layout

public interface IConcludable { }

public struct Conclusion<T> where T : IConcludable
{
    public bool IsSuccessful;
    public T Result;

    // Constructors, members, etc.
}

public class ErrorReport : IConcludable { ... }

public class MathArg : IConcludable { ... }

public class ParseResult : IConcludable { ... }

Implementation

public Conclusion<ParseResult> ParseInput (string input)
{
    // Parse input...
    // Initialize ParseResult object...

    return new Conclusion<ParseResult>(result);
}

Problem

When I get the final value, it is a built-in type like int, double, string, bool, etc. I would like to use Conclusion<T> as a return because I have a class that handles error reports when the input string is invalid:

if (conclusion.ReturnObject is ErrorReport)
{
    ErrorManager errorManager = new ErrorManager();
    errorManager.Resolve(conclusion);
}

Research

I looked into constraints.

It appears that constraints only add together, so including the interfaces of each built-in type that I need would require defining a mountain of methods that do not have anything to do with my Conclusion struct.


Extension methods

These actually change the behavior of built-in types. It is not what I am looking for because my interface, IConcludable, does not have any methods.

Replacing built-in types

Not possible. I don't need to change the behavior of these types though. I just want to add an empty interface to it.

There does not seem to be anything regarding adding an interface to a built-in type. I'm not sure if "Inheritance" is what it would be referred to. Is this possible?

Edit

Better explanation of Conclusion struct

I am using the conclusion struct as a return object in most of my methods. This is because I am using delegates. See the actual code of the objects below:

public delegate Conclusion<T> Validator<T>(T subclass) where T : IVerifiable<T>;

public delegate Conclusion<BaseFunction> Executor(BaseFunction subclass);

public struct Conclusion<T> where T : IConcludable
{
    public bool IsSuccessful;
    public T ReturnObject;

    public Conclusion(T returnObject)
    {
        this.ReturnObject = returnObject;
        this.IsSuccessful = returnObject is Error ? false : true;
    }
}

public class BaseFunction : IVerifiable<BaseFunction>, IConcludable
{
    public List<BaseArgument> Args;
    public Executor Executing;
    public Validator<BaseFunction> Validating;
    public string UserInput;

    public Conclusion<BaseFunction> Validate(BaseFunction subclass)
    {
        if (this.Validating != null)
        {
            return Validating(subclass);
        }
        else
        {
            StringBuilder message = new StringBuilder();
            message.Append("A Validating delegate has not been assigned.");

            throw new InvalidOperationException(message.ToString());
        }
    }

    public Conclusion<BaseFunction> Execute(BaseFunction subclass)
    {
        if (this.Executing != null)
        {
            return this.Executing(subclass);
        }
        else
        {
            StringBuilder message = new StringBuilder();
            message.Append("An Executing delegate has not been assigned.");

            throw new InvalidOperationException(message.ToString());
        }
    }
}

public class Function<T> : BaseFunction
{
    public T Result;

    public Function()
    {
        base.Args = new List<BaseArgument>();
    }
}

public class BaseArgument : IVerifiable<BaseArgument>, IConcludable
{
    public string Role;
    public string UserInput;
    public int Position;
    public Validator<BaseArgument> Validating;

    public Conclusion<BaseArgument> Validate(BaseArgument subclass)
    {
        if (this.Validating != null)
        {
            return Validating(subclass);
        }
        else
            throw new InvalidOperationException();
    }
}

public class Argument<T> : BaseArgument
{
    public T Value;

    public Argument(int position)
    {
        base.Position = position;
    }
}

public static class ExecutionHandler
{
    public static Conclusion<BaseFunction> Sum(BaseFunction subclass)
    {
        subclass = (Function<double>)subclass;

        // Execution code.

        return new Conclusion<BaseFunction>(subclass);
    }

    public static Conclusion<BaseFunction> Concatenate(BaseFunction subclass)
    {
        subclass = (Function<double>)subclass;

        // Execution code.

        return new Conclusion<BaseFunction>(subclass);
    }
}

If I need to post more, I will. but it's really a lot to look at. The return type of the methods assigned by all of the delegates I use have a return type of Conclusion<T> so that I can have a return object as well as an error if one occurs. The functions in the code above return Conclusion<BaseFunction>, but that return is converted into an object Addend<T> if it is a number. If it is a part of another type of function that returns a string or bool or other type, it is converted into a different type of class. By the end of a numeric calculation, the return will be something like Conclusion<int> or Conclusion<double>. So adding int and double to the IConcludable interface is what I am trying to do.

Better explanation of the application

I am writing a C# console application. It takes input from the user, and writes an answer. The input is similar to Excel formulas: Sum(5, 15, Average(2, 3), 5) or Concatenate("5 + 5 = ", Text(Sum(5, 5))). The input string is validated, parsed, and returns a result.

Upvotes: 10

Views: 2132

Answers (2)

Kien Chu
Kien Chu

Reputation: 4895

UPDATED (ADD MORE EXPLANATION)

As requested, I want to explain a bit more about my last answer.

Requirements

  • The Conclusion need to support both value type and reference type
  • Generic
  • Value Types: all numeric data types (int, short, long, etc), boolean, char, date....
  • Reference Types: string and user-defined class (in OP's sample, IConcludable)

Solution:

  • Introduce a base class (AbstractConclusion) that accepts Object as generic input
  • Move the logic to base class for reuse
  • Introduce two new concrete implementations that accepts struct and IConcluable (have the ability to add more implementation, for ex: string)
  • The inherited classes are able to all the methods of base class

ORIGINAL ANSWER:

You can put the logic in AbstractConclusion class, and have two implementations of it (Conclusion which accepts IConcludeable and PrimitiveConclusion which accepts struct data type)

See code sample below:

void Main()
{
    PrimitiveConclusion<int> primitiveConclusion = new PrimitiveConclusion<int>(1);
    Conclusion<ParseResult> parseResultConclusion = new Conclusion<ParseResult>(new ParseResult {});

    Console.WriteLine($"{primitiveConclusion.Result.GetType()}");
    Console.WriteLine($"{parseResultConclusion.Result.GetType()}");
}

public class TestClass
{
    public Conclusion<ParseResult> ParseInput(string input)
    {
        return new Conclusion<ParseResult>(null);
    }
}

public interface IConcludable { }

public abstract class AbstractConclusion<T>
{
    public AbstractConclusion(T t)
    {
        IsSuccessful = t != null;
        Result = t;
    }
    public bool IsSuccessful;
    public T Result;
}

public class Conclusion<T> : AbstractConclusion<T> where T : IConcludable
{
    public Conclusion(T t) : base(t)
    {
    }
}


public class PrimitiveConclusion<T> : AbstractConclusion<T> where T : struct
{
    public PrimitiveConclusion(T t) : base(t)
    {
    }
}


public class ParseResult : IConcludable { }

Upvotes: 1

Mikhail Tulubaev
Mikhail Tulubaev

Reputation: 4261

With where T : IConcludable generic type constraint you can not pass int as generic argument. And also there is no way to append an interface to primitive type.

You can do some workaround to do this. You can put the main logic of Conclusion into base abstract class and inherit from it, restructure Conclusion as class (because structs not support inheritance) and create classes

public class Conclusion<T> : BaseConclusion
where T : IConcludable

and

public class PrimitiveConclusion<T> : BaseConclusion
where T : struct

and also you should create another class for string, as string is not struct and not implement IConcludable

As I know, there is no another way to pass different types as generic arguments.


Also, instead of creating Conclusion class for built-in types you can create wrapper struct, which will implement IConcludable. And have a deal with it

public struct Wrapper<T> : IConcludable
{
    public T Value { get; set; }
}

Upvotes: 0

Related Questions