我为游戏的设置屏幕设置了一组Option对象。 Option类的泛型类型对于每个子类都不同。
当前的类如下:
public interface IOption
{
string GetName();
void SetName(string name);
}
//Note that this does not inherit from the Option class
public class ExitOption : IOption
{
private string name;
public ExitOption(string name) => this.name = name;
public string GetName()
{
return name;
}
public void SetName(string name)
{
this.name = name;
}
}
public class Option<T> : IOption
{
public string Name;
private T value;
private T defaultValue;
public T[] Values;
public Option(string name, T defaultValue, params T[] values)
{
(Name, value, this.defaultValue, Values) = (name, defaultValue, defaultValue, values);
}
public T GetValue()
{
return value;
}
public void SetValue(T value)
{
this.value = value;
}
public string GetName()
{
return Name;
}
public void SetName(string name)
{
this.Name = name;
}
}
public class IntegerOption : Option<int>
{
private int minValue;
private int maxValue;
// Creates an array of numbers between the min and max value
public IntegerOption(string name, int defaultValue, int minValue, int maxValue) : base(name, defaultValue, RangeCreator.IntegerRange(minValue, maxValue))
{ }
}
//"Key" is an enum
public class KeyOption : Option<Key>
{
// Creates an array containing all enum constants
public KeyOption(string name, Key defaultValue) : base(name, defaultValue, RangeCreator.EnumRange<Key>())
{}
}
}
我这样构造对象:
ExitOption exit = new ExitOption("Exit");
IntegerOption volume = new IntegerOption("Volume", 100, 0, 100);
KeyOption jump = new KeyOption("Jump", Key.Spacebar);
并将它们放在列表中:
List<IOption> options = new List<IOption>();
options.Add(exit);
options.Add(volume);
options.Add(jump);
例如,当我想遍历所有选项并将其值更改为它们范围内的最后一个值,或进行任何类型的值更改时,就会出现问题。在Java中,我将执行以下操作:
for(IOption option : options)
{
if(option instanceof ExitOption)
{
//Handle exiting the menu
}
else
{
//Type is unkown, therefore I do not provide any type arguments
Option currentOption = (Option) option;
currentOption.SetValue(currentOption.Values[currentOption.Values.length - 1]);
}
}
我将如何在C#中完成类似的事情?
答案 0 :(得分:2)
这是“丑陋的基类”版本,以及一些C#更改:
using System;
using System.Collections.Generic;
using System.Linq;
public interface IOption
{
string Name { get; set; }
}
public class ExitOption : IOption
{
public ExitOption(string name) => Name = name;
public string Name { get; set; }
}
public abstract class Option : IOption
{
public Option(string name) => Name = name;
public string Name { get; set; }
public abstract object ObjValue { get; set; }
public abstract IEnumerable<object> ObjValues { get;}
}
public class Option<T> : Option
{
private T _value;
private List<T> _values;
public Option(string name, T initialValue, params T[] values) : base(name)
{
(_value, _values) = (initialValue, values.ToList());
}
public override IEnumerable<object> ObjValues { get => _values.Cast<object>().AsEnumerable(); }
public T Value { get => _value; set => _value = value; }
public override object ObjValue { get => _value; set => _value = (T)value; }
public IEnumerable<T> Values => _values.AsEnumerable();
}
当您希望/不需要通用参数就可以工作时,可以使用Option
类型及其ObjXxx
属性来访问其值。但是,当确实有通用类型参数可用时,Option<T>
仍然是访问此数据的首选方法。
我仍然有点怀疑Name
易变...
答案 1 :(得分:0)
在避免评论所选的类设计的同时,您可以向IOption
添加一个方法,该方法完全可以实现您想要的功能。实际上,这毕竟是接口的目的-向消费者隐藏这样的细节。如果您要做的就是将值更新为该范围内的最后一个值,则执行以下操作应支持该操作:
public interface IOption
{
string GetName();
void SetName(string name);
// or some other appropriate name for this
void SetValueToLastInRange();
}
似乎函数本身不需要调用站点的输入。也许它也可以有一个更好的名称来包含退出行为。
编辑:我认为重要的是要强调这种行为不应由呼叫站点决定。数据保存在IOption
实现中。您要从IOption
中取出它只是将其直接传递回IOption
中-为什么还要公开数据呢?通常,我发现它减少了耦合,从而限制了公开的数据,而是公开了行为。您已经为此设置了接口,您所缺少的只是IOption
上的新方法,它可以完成您需要做的事情-然后每个实现都可以以自己的特殊方式来做(看来是对于每个实现异常出口都相同)。
答案 2 :(得分:0)
创建具有object
个值实例的其他接口
public interface IValueOption : IOption
{
void SetValue(object value);
object GetValue();
object[] Values { get; }
}
然后您的选项将实现此接口:
public class Option<T> : IOption, IValueOption
{
object[] IValueOption.Values => Values.Cast<object>().ToArray();
void IValueOption.SetValue(object value)
{
SetValue((T)value);
}
object IValueOption.GetValue()
{
return GetValue();
}
...
}
当然,这不能让您更改Values数组,但可以使用它们并调用SetValue / GetValue。
foreach(IOption option in options)
{
if(option is ExitOption)
{
//Handle exiting the menu
}
else if(option is IValueOption)
{
//Type is unkown, therefore I do not provide any type arguments
IValueOption currentOption = (IValueOption)option;
currentOption.SetValue(currentOption.Values[currentOption.Values.Length - 1]);
}
}