Reputation: 853
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
Reputation: 4895
UPDATED (ADD MORE EXPLANATION)
As requested, I want to explain a bit more about my last answer.
Requirements
Conclusion
need to support both value type and reference typeIConcludable
)Solution:
AbstractConclusion
) that accepts Object
as generic inputstruct
and IConcluable
(have the ability to add more implementation, for ex: string)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
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 struct
s 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