我必须根据构造函数的参数构建一个特殊的类行为。假设Foo绘图应该变成永远绿色,如果它是用绿色铅笔构建(绘制)的话。 如果使用任何铅笔,Foo应该是透明的......
现在,请看下面的代码。是否有可能修改构造函数“看到”传入参数对象的实际类型的“输出”? (实际上它们都是“对象”):
class Program
{
static void Main(string[] args)
{
object[] objs = { new IndexOutOfRangeException(), MyEnum.Beta, 45, new AssemblyName(), new { Name = "a" } };
for (int i = 0; i < objs.Length; i++)
{
Console.WriteLine("{0} => {1} ", i, objs[i]);
}
Console.WriteLine("=========================== ");
for (int i = 0; i < objs.Length; i++)
{
Foo myFoo = new Foo(objs[i]);
Console.WriteLine("{0} => {1}", i, myFoo);
}
}
}
public class Foo
{
object value;
string typeName;
public Foo(object obj)
{
value = obj;
typeName = "object";
}
public Foo(MyEnum enm)
{
value = enm;
typeName = "MyEnum";
}
public Foo(int myInt)
{
value = myInt;
typeName = "int";
}
public Foo(Exception ex)
{
value = ex;
typeName = "exception";
}
public override string ToString()
{
return string.Format("FOO (object = '{0}'; type = '{1}')", value, typeName);
}
}
public enum MyEnum
{
Alpha,
Beta
}
输出
0 => System.IndexOutOfRangeException: Index was outside the bounds of the array.
1 => Beta
2 => 45
3 =>
4 => { Name = a }
===========================
0 => FOO (object = 'System.IndexOutOfRangeException: Index was outside the bound
s of the array.'; type = 'object')
1 => FOO (object = 'Beta'; type = 'object')
2 => FOO (object = '45'; type = 'object')
3 => FOO (object = ''; type = 'object')
4 => FOO (object = '{ Name = a }'; type = 'object')
修改
正如看到一些答案,我想强调的是,不是要在“type”变量中显示正确的字符串,比如使用value.GetType()
,而是在正确的构造函数中“输入”是问题
换句话说,为什么编译器没有检测到正确的类型并将其“重定向”到正确的构造函数?
编辑2:
正如回答者所提到的,构造函数的“方式”是在编译时“构建”的,而不是在运行时。说像他的代码
MyEnum en = MyEnum.Beta;
Console.WriteLine("Enum example: obj:{0} Foo:{1}", en, new Foo(en));
将输出“好”输出:
Enum example: obj:Beta Foo:FOO (object = 'Beta'; type = 'MyEnum')
所以......显然,任何方式都可以“绕过”这种行为但是在构造函数中运行时检测,就像提议的Reed Copsey ......?!
答案 0 :(得分:3)
为什么编译器没有检测到正确的类型并将其“重定向”到正确的构造函数?
这是因为您将对象作为System.Object
传递。 (object[] objs = { n...
)构造函数在编译时选择 ,而不是在运行时。编译器可以看到所使用变量的声明,并用于检查相应的类型。
正如你在另一条评论中提到的那样:
好吧,如果我有大量的对象,并且不知道,先验他们的类型怎么办?
这正是编译器以这种方式工作的原因。它在编译时无法知道你想要哪个构造函数,并且因为你有一个System.Object
构造函数可以工作,所以它会被选中。
如果要单独处理这些特定类型,但仍然将对象构造为System.Object
,则必须在对象的构造函数内部添加检查并分别处理特定情况。但是,如果你这样做,这不是最易维护的代码。
public Foo(object obj)
{
value = obj;
typeName = "object";
// Change typeName if appropriate
if (obj != null)
{
if (obj is MyEnum)
typeName = "MyEnum";
else if (obj is int)
typeName = "int";
else if (obj is Exception)
typeName = "exception";
}
}
编辑:
鉴于此,在您的实际代码中,构造函数可能会做更多的工作,我会考虑使用工厂方法来处理这个问题。它允许您使用与上面类似的方法,但保留类型安全构造函数:
// I'd make the object constructor private, to prevent accidental usage:
private Foo(object obj) { ...
public static Foo CreateAppropriateFoo(object obj)
{
if (obj == null)
return new Foo(obj); // Use object constructor
else
{
if (obj is MyEnum)
return new Foo( (MyEnum)obj );
else if (obj is int)
return new Foo( (int)obj );
else if (obj is Exception)
return new Foo( (Exception)obj );
}
}
至少,这可以防止构造函数逻辑的重复,并且使得在运行时发生某些逻辑更加明显。
答案 1 :(得分:0)
我认为你可以通过删除接受对象参数的ctor(然后一切都应该正确绑定到正确的构造函数),或者通过将参数转换为正确的类型来获得你想要的东西,例如这个:
var myFoo = new Foo(objs[0] as Exception);
很可能会使用带有Exception参数的ctor。
编辑:您的问题:为什么编译器检测不到正确的类型并将其“重定向”到正确的构造函数?
我的回答:因为你正在传递一个对象数组的成员,所以编译器正确将该实际对象实例作为对象处理。
就像catch块一样,必须从最通用到更一般的顺序排序,你有一个顶级的通用捕手让你的ctor接受一个对象,你不应该对从对象数组传递一个值感到惊讶编译器使用最合适的候选者。这就是为什么我建议你在调用ctor时进行强制转换,这样你就可以精确地告诉你想要将objs [0]转换为异常对象,或者编译器会将它视为对象。