Gri*_*zly 0 c# oop class data-structures
这是一个设计问题,我无法想象任何优雅的解决方案,您将如何创建所描述的类,因此,作为利用的示例,在实例化时您可以调用打印函数,如果它是 int,则只打印数字,如果它为空,则不会执行任何操作,如果它是一个数组,则打印该数组的第 i 个(我猜是内部迭代器)元素。这里什么都没有,提前谢谢!
感谢大家提出的答案,更具体地说明要求。
举个例子:[[1,2,3], null, 4, 5, [6,null,7]]这里的所有元素都是MyIntOrArray包括主数组本身的类型,因此当调用 print 7 次时,您将得到诸如“1”“2”“3”“4”“5”之类的输出“6” “7”
无论如何,我对Print功能不那么感兴趣,但对如何创建此类需求的实现/结构/类更感兴趣。
public class MyIntOrArray
{
public int? Value { get; set; }
public MyIntOrArray[] Values { get; set; }
private bool AmIArray { get; set; }
private int InnerIndex { get; set; }
public MyIntOrArray(int? value)
{
Value = value;
AmIArray = false;
}
public MyIntOrArray(int size, bool isArray)
{
Values = new MyIntOrArray[size];
AmIArray = true;
}
public bool Print()
{
if(AmIArray)
{
if (InnerIndex < Values.Length)
{
if (Values[InnerIndex].Print())
InnerIndex += 1;
return InnerIndex >= Values.Length ? true : false;
}
}
else if(Value != null)
{
Console.WriteLine(Value.ToString());
}
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
这里的大多数答案似乎都忽略了一个奇怪的要求,即如果内容是一个数组,该Print方法应该打印一个值并推进一个内部指针。我将在下面处理这个要求。
然而,OP 并没有指定如果调用Print足够多次来耗尽数组会发生什么。我假设在这种情况下,该方法不应再打印任何内容,但另一种选择是回绕并从头开始。
我将给出一个专业且通用的答案。
考虑到要求,您可以利用数组,包括其他两种替代方案作为特殊情况:Null(或无)是空数组,value是单例(单元素)数组。
因此,专门的实现可能如下所示:
public sealed class IntOrArrayOrNothing
{
private readonly int[] ints;
private int idx;
public IntOrArrayOrNothing()
{
ints = Array.Empty<int>();
}
public IntOrArrayOrNothing(int i)
{
ints = new[] { i };
idx = -1; // Sentinel value
}
public IntOrArrayOrNothing(int[] ints)
{
this.ints = ints;
}
public void Print()
{
if (ints.Length == 0 || ints.Length <= idx)
return;
if (idx == -1) // Check sentinel value indicating a single int
{
Console.WriteLine(ints[0]);
return;
}
Console.WriteLine(ints[idx++]);
}
}
Run Code Online (Sandbox Code Playgroud)
对这种专门实现的一个公平的批评是它不容易改变或扩展。如果您想在到达末尾时将索引回零怎么办?如果想一次性打印数组怎么办?如果您想做打印以外的其他事情怎么办?
作为一般规则,每当您遇到类型应该是互斥替代方案之一的要求时,您就需要sum 类型。
有些语言本身就支持这些。例如,在F#中,您只需声明如下类型:
type IntOrArrayOrNothing = Nothing | Int of int | Array of int array
Run Code Online (Sandbox Code Playgroud)
然而,也许值的实际类型(这里是int)并不重要,在这种情况下,您可以将类型设为泛型(或者参数多态):
type ValueOrCollectionOrNothing<'a> = Nothing | Value of 'a | Collection of 'a seq
Run Code Online (Sandbox Code Playgroud)
不幸的是,像 C# 这样的语言不支持开箱即用的求和类型,但并非所有内容都会丢失。您可以通过(至少)两种不同的方式之一实现自己的求和类型:
这些替代方案实际上是同构的。Church 编码更简洁,但由于这个问题被标记为oop,所以我将显示 Visitor 实现。
在这种特殊情况下,确实没有必要花这么多时间,因为即使使用泛型,无值和值的情况也可以使用集合来实现。尽管如此,我仍将继续演示链接文章中讨论的一般技术如何应用于具体案例。
我还将代码实现为不可变值,因为可变状态通常只会使代码推理变得更加困难。
因此,首先声明一个类:
public sealed class ValueOrCollectionOrNothing<T>
{
private readonly IValueOrCollectionOrNothing imp;
public ValueOrCollectionOrNothing()
{
imp = new Nothing();
}
public ValueOrCollectionOrNothing(T value)
{
imp = new Value(value);
}
public ValueOrCollectionOrNothing(IReadOnlyCollection<T> values)
{
imp = new Collection(values);
}
public TResult Accept<TResult>(
IValueOrCollectionOrNothingVisitor<T, TResult> visitor)
{
return imp.Accept(visitor);
}
private interface IValueOrCollectionOrNothing
{
TResult Accept<TResult>(
IValueOrCollectionOrNothingVisitor<T, TResult> visitor);
}
// Implementation classes (see below) go here...
}
Run Code Online (Sandbox Code Playgroud)
请注意,三个构造函数重载初始化了IValueOrCollectionOrNothing接口的三个不同实现(见下文)。
该IValueOrCollectionOrNothingVisitor界面是您实际“枚举”互斥情况的地方:
public interface IValueOrCollectionOrNothingVisitor<T, TResult>
{
TResult VisitNothing();
TResult VisitValue(T value);
TResult VisitCollection(IReadOnlyCollection<T> collection);
}
Run Code Online (Sandbox Code Playgroud)
该Accept方法需要一个访问者,并且只委托给imp.Accept。
该类Nothing是一个嵌套类:
private sealed class Nothing : IValueOrCollectionOrNothing
{
public TResult Accept<TResult>(
IValueOrCollectionOrNothingVisitor<T, TResult> visitor)
{
return visitor.VisitNothing();
}
}
Run Code Online (Sandbox Code Playgroud)
这同样适用于Value:
private sealed class Value : IValueOrCollectionOrNothing
{
private readonly T value;
public Value(T value)
{
this.value = value;
}
public TResult Accept<TResult>(
IValueOrCollectionOrNothingVisitor<T, TResult> visitor)
{
return visitor.VisitValue(value);
}
}
Run Code Online (Sandbox Code Playgroud)
和Collection:
private sealed class Collection : IValueOrCollectionOrNothing
{
private readonly IReadOnlyCollection<T> collection;
public Collection(IReadOnlyCollection<T> collection)
{
this.collection = collection;
}
public TResult Accept<TResult>(
IValueOrCollectionOrNothingVisitor<T, TResult> visitor)
{
return visitor.VisitCollection(collection);
}
}
Run Code Online (Sandbox Code Playgroud)
访客会是什么样子?
如果你想打印整个集合,你可以这样做:
public sealed class PrintVisitor : IValueOrCollectionOrNothingVisitor<int, IEnumerable<string>>
{
public IEnumerable<string> VisitNothing()
{
return Array.Empty<string>();
}
public IEnumerable<string> VisitValue(int value)
{
return new[] { value.ToString() };
}
public IEnumerable<string> VisitCollection(IReadOnlyCollection<int> collection)
{
return collection.Take(1).Select(i => i.ToString());
}
}
Run Code Online (Sandbox Code Playgroud)
另一方面,如果您想推进集合索引,您可以这样做:
public sealed class PrintAndChangeStateVisitor
: IValueOrCollectionOrNothingVisitor<int, (IReadOnlyCollection<string>, PrintAndChangeStateVisitor)>
{
private readonly int idx;
public PrintAndChangeStateVisitor() : this(0)
{
}
private PrintAndChangeStateVisitor(int idx)
{
this.idx = idx;
}
public (IReadOnlyCollection<string>, PrintAndChangeStateVisitor) VisitNothing()
{
return (Array.Empty<string>(), this);
}
public (IReadOnlyCollection<string>, PrintAndChangeStateVisitor) VisitValue(
int value)
{
return (new[] { value.ToString() }, this);
}
public (IReadOnlyCollection<string>, PrintAndChangeStateVisitor) VisitCollection(
IReadOnlyCollection<int> collection)
{
if (collection.Count <= idx)
return (Array.Empty<string>(), this);
return (
new[] { collection.ElementAt(idx).ToString() },
new PrintAndChangeStateVisitor(idx + 1));
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,这并不要求您更改ValueOrCollectionOrNothing<T>类;只需更改该类即可。你只需写另一个访客即可。因此,这样的设计更具可扩展性。