你如何在C#中直接输入一个盒装结构?

时间:2010-10-21 13:27:28

标签: c# struct implicit-cast

我有一个结构的命名空间,代表各种度量单位(米,英尺,英寸等)......总共12个,由T4模板生成:)。

每个struct都带有隐式转换运算符,以支持将值转换为任何其他度量值类型,因此以下sytax是合法的:

var oneThousandMeters = new Meters(1000);    
Kilometers aKilo = oneThousandMeters ;     // implicit cast OK. Value = 1 Km

为了增添乐趣,有一个名为 Distance 的全能类,它可以包含任何度量单位,也可以隐式地转换为和测量值......

var magnum = new Distance(12, DistanceUnits.Inches); 
Feet wifesDelight = magnum;               // implicit cast OK. Value = 1 foot.

遵循.NET框架标准,所有字符串格式化和解析都由外部FormatProvider处理,后者实现ICustomFormatter。遗憾的是,这意味着该值在传递给Format方法时被装箱,并且format方法需要针对每个已知的测量类型测试对象,然后才能对其进行操作。在内部,Format方法只是将测量值转换为距离值,所以问题就在这里......

问题:

public string Format(string format, object arg, IFormatProvider formatProvider)
{
    Distance distance;           

    // The following line is desired, but fails if arg != typeof(Distance)   
    distance = (Distance)arg;    

    // But the following tedious code works:
    if(arg is Distance)
       distance = (Distance)arg;
    else if(arg is Meters)
       distance = (Distance)(Meters)arg;     // OK. compile uses implicit cast. 
    else if(arg is Feet)
       distance = (Distance)(Feet)arg;       // OK. compile uses implicit cast. 
    else if(arg is Inches)
       distance = (Distance)(Inches)arg;     // OK. compile uses implicit cast. 
    else
        ... // tear you hair out for all 12 measurement types
}

对此有任何解决方案,还是这只是价值类型无法解决的缺点之一?

PS:我已经检查了this post,虽然问题类似,但这不是我想要的。

2 个答案:

答案 0 :(得分:3)

嗯,这是将拆箱转换与用户定义转换分开的问题。您希望两者都发生 - 您必须指定要取消选择的类型,以及让编译器知道何时需要用户定义的转换。除非您使用动态类型,否则必须在 compile 时选择用户定义的转换,因此编译器需要知道它尝试转换的类型。

一个选项是拥有所有结构实现的IDistance 接口。然后你可以使用:

IDistance distanceArg = arg as IDistance;
if (distanceArg != null)
{
    Distance distance = distanceArg.ToDistance();
}

由于你已经有了盒装价值,使用界面不会导致额外的拳击或类似的东西。每个ToDistance实现可能只使用隐式转换:

public Distance ToDistance()
{
    return this;
}

...或者您可以使用ToDistance进行转化。

答案 1 :(得分:1)

是的,这只是你必须忍受的一件事。

如果将整数推送到对象中,则会遇到同样的问题:

int a = 0;
object b = a;
int c = (int)b; // this works
short d = (short)b; // this fails
short e = (short)(int)b; // this works