тип, который может содержать либо один тип или другой, обычно называют (Unsurprisingly) Either
. Это особый случай sum type, в основном дискриминированный союз, tagged union, или несвязанный союз с ровно двумя случаями (вместо произвольного числа).
К сожалению, в стандартных библиотеках не существует реализация типа Either
, но есть множество реализаций, которые можно найти в Google, GitHub и других местах ... и портирование одной из существующих реализаций, например. Хаскелл или Скала не так уж и трудны.
Это выглядит как это (простите мой код, я не знаю, на самом деле C♯, что хорошо):
using System;
abstract class Either<A, B>
{
public abstract bool IsLeft { get; }
public abstract bool IsRight { get; }
public abstract A Left { get; }
public abstract B Right { get; }
public abstract A LeftOrDefault { get; }
public abstract B RightOrDefault { get; }
public abstract void ForEach(Action<A> action);
public abstract void ForEach(Action<B> action);
public abstract void ForEach(Action<A> leftAction, Action<B> rightAction);
private sealed class L : Either<A, B>
{
private A Value { get; }
public override bool IsLeft => true;
public override bool IsRight => false;
public override A Left => Value;
public override B Right { get { throw new InvalidOperationException(); } }
public override A LeftOrDefault => Value;
public override B RightOrDefault => default(B);
public override void ForEach(Action<A> action) => action(Value);
public override void ForEach(Action<B> action) {}
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => leftAction(Value);
internal L(A value) { Value = value; }
}
private sealed class R : Either<A, B>
{
private B Value { get; }
public override bool IsLeft => false;
public override bool IsRight => true;
public override A Left { get { throw new InvalidOperationException(); } }
public override B Right => Value;
public override A LeftOrDefault => default(A);
public override B RightOrDefault => Value;
public override void ForEach(Action<A> action) {}
public override void ForEach(Action<B> action) => action(Value);
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => rightAction(Value);
internal R(B value) { Value = value; }
}
public static Either<A, B> MakeLeft(A value) => new L(value);
public static Either<A, B> MakeRight(B value) => new R(value);
}
И вы бы использовать его как это:
static class Program
{
public static void Main()
{
var input = Console.ReadLine();
int intResult;
var result = int.TryParse(input, out intResult) ? Either<int, string>.MakeLeft(intResult) : Either<int, string>.MakeRight(input);
result.ForEach(r => Console.WriteLine("You passed me the integer one less than " + ++r), r => Console.WriteLine(r));
}
}
Может вам лучше описать, почему именно это вам нужно? (Вы всегда можете вернуть «объект», но я думаю, может быть, вам стоит переосмыслить немного дизайна). –
«var» - это просто «компилятор», пожалуйста, выясните правильный тип данных и поместите его здесь ». Он все еще должен быть определен * во время компиляции *. –