假设我有一个数据对象,但是这个对象可以包含几种类型的数据之一。
class Foo
{
int intFoo;
double doubleFoo;
string stringFoo;
}
现在,我想创建一个存取器。获取此数据的一些方法。显然,我可以创建多个访问者:
public int GetIntFoo();
public double GetDoubleFoo();
public string GetStringFoo();
或者我可以创建多个属性
public int IntFoo { get; set; }
public double DoubleFoo { get; set; }
public string StringFoo { get; set; }
我不认为这是一个非常好的设计。它要求客户端代码更关注类型而不是它本来应该是什么。更重要的是,我真的只需要这个类的一个值,上面的内容将允许同时分配每种类型中的一个。不好。
一种选择是使用泛型。
class Foo<T>
{
public T TheFoo { get; set; }
}
但是,这不会创建Foo,它会创建一个Foo&lt; T&gt;。每个都有不同的类型,所以我不能真正将它们用作相同的类型。
我可以推导出Foo&lt; T&gt;来自FooBase,然后将它们全部视为FooBase,但后来我又回到了访问数据的问题。
不同的泛型选项是使用以下内容:
class Foo
{
string stringRepresentationOfFoo;
public T GetFoo<T>() { return /* code to convert string to type */ }
}
当然问题是任何一种T都可以通过,坦白说,它有点忙。
我也可以只输入值并返回一个对象,但是没有类型安全。
理想情况下,我想对所有Foo都一样,但我希望类型安全,这样如果没有StringFoo,我甚至无法编译对StringFoo的引用。
Foo foo = new Foo("Foo");
string sFoo = foo.Value; // succeeds.
Foo foo = new Foo(0);
int iFoo = foo.Value; // succeeds
string sFoo = foo.Value; // compile error
也许这甚至不可能......我将不得不做出一些妥协,但也许我错过了一些东西。
有什么想法吗?
编辑:
好吧,正如daniel指出的那样,运行时类型的编译时间检查是不实际的。
在这里做我想做的事情,我最好的选择是什么?也就是说,对待所有Foo是一样的,但仍然有一个相对理智的访问机制?
EDIT2:
我不想将值转换为不同的类型。我想为值返回正确的类型。也就是说,如果它是一个double,我不想返回一个int。
答案 0 :(得分:4)
如何将变量作为参数传递给get?像这样:
int i = foo.get(i);
然后在你的课堂上,你会有类似的东西:
public int get(int p) {
if(this.type != INTEGER) throw new RuntimeException("Data type mismatch");
return this.intVal;
}
public float get(float p) {
if(this.type != FLOAT) throw new RuntimeException("Data type mismatch");
return this.floatVal;
}
这种类型的内部输出检查:不是检查foo的类型,而是检查你想要的类型。如果它可以为您提供该类型,那么它会抛出运行时异常。
答案 1 :(得分:3)
我认为这不起作用(给你想要的编译器错误)
你想要做什么:
Foo bar = (new Random()).Next(2) == 0 ? new Foo("bar") : new Foo(1);
int baz = bar.Value;
这是编译错误吗?
我认为“对待它们都是一样的”(至少按照你描述的方式)和“编译时间错误”将是相互排斥的。
无论如何,我认为“最佳方式”将是泛型和继承之间的妥协。您可以定义{F}的子类Foo<T>
;那么你仍然可以拥有Foo
的集合。
abstract public class Foo
{
// Common implementation
abstract public object ObjectValue { get; }
}
public class Foo<T> : Foo
{
public Foo(T initialValue)
{
Value = initialValue;
}
public T Value { get; set; }
public object ObjectValue
{
get { return Value; }
}
}
答案 2 :(得分:1)
许多系统使用辅助方法返回备用类型,就像.net框架基础对象具有ToString()方法一样
选择哪个是您的每个对象的最佳基本类型,并为其他情况提供To方法
e.g。
class Foo{
public Int32 Value { get; set; }
public Byte ToByte() { return Convert.ToByte(Value); }
public Double ToDouble() { return (Double)Value; }
public new String ToString() { return Value.ToString("#,###"); }
}
答案 3 :(得分:0)
有一件事是将任何类型存储在类的内部状态中,另一种是在外部公开它。当你写一个类时,你实际上是在为它的行为声明一个合同。您编写它的方式将极大地影响客户端代码在使用该类时的外观。
例如,通过实现IConvertible接口,您可以声明您的类型可以转换为任何CLR类型作为等效值。
我还看到过使用Value类来存储计算结果的实现,结果可以表示字符串,double,int或boolean。但是,问题是客户端代码必须检查枚举{String,Integer,Double,Boolean}的Value.Type属性,然后转换Value.Value属性(由Value类作为Object类型在外部公开) )或使用特定的ValueString,ValueDouble,ValueInt,ValueBoolean getters。
答案 4 :(得分:0)
为什么不使用string
,double
和int
?
关于收集的信息:使用object
怎么样?无论如何,您将不得不检查类型等。为了帮助您,您可以使用is
和as
运算符。 Enumerable.Cast Method,甚至更好,Enumerable.OfType Method。
答案 5 :(得分:0)
实际上,这门课的目的是什么?最大的问题似乎是设计至少破坏了SRP(单一责任原则)。
尽管如此,如果我正确地阅读它,你想在容器中存储一些值,将容器传递给客户端并键入 - 安全地检索值。
使用这种方法,您可以使用您的提议,即
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
Foo a = new Foo();
a.SetValue(4);
Console.WriteLine(a.GetValue<int>());
Foo b = new Foo();
a.SetValue("String");
Console.WriteLine(a.GetValue<string>());
Console.ReadLine();
return 0;
}
}
class Foo {
private object value; // watch out for boxing here!
public void SetValue(object value) {
this.value = value;
}
public T GetValue<T>() {
object val = this.value;
if (val == null) { return default(T); } // or throw if you prefer
try {
return (T)val;
}
catch (Exception) {
return default(T);
// cast failed, return default(T) or throw
}
}
}
}
但是,在这种情况下,为什么不简单地将数据作为对象传递并自己投射?
根据您的需要,您也可以尝试使用“C#中的PHP”:
namespace Project1 {
public class Class1 {
static int Main(string[] args) {
MyInt a = 1;
MyInt b = "2";
Console.WriteLine(a + b); // writes 3
Console.ReadLine();
return 0;
}
}
class MyInt {
private int value;
public static implicit operator int(MyInt container) {
return container.value;
}
public static implicit operator MyInt(int value) {
MyInt myInt = new MyInt();
myInt.value = value;
return myInt ;
}
public static implicit operator MyInt(string stringedInt) {
MyInt myInt = new MyInt();
myInt.value = int.Parse(stringedInt);
return myInt;
}
}
}
答案 6 :(得分:0)
对不起,我只是不买你的前提。如果数据都具有相同的目的,那么它们应该都具有相同的类型。考虑一个意味着保持当前温度的类,由几个Web服务之一返回。所有服务都以摄氏温度返回。但是一个以int形式返回,一个以double形式返回,一个以字符串形式返回。
这不是三种不同的类型 - 它是一种类型 - 双重。你只需要将非双返回转换为double,这就是温度(或浮点数)。
一般来说,如果你对一件事有多种表示,那么它仍然是一件事,而不是多件事。将多个表示转换为一个。