C# 属性未调用隐式/显式转换运算符(System.ComponentModel.DataAnnotation.dll)

C# 属性未调用隐式/显式转换运算符(System.ComponentModel.DataAnnotation.dll),c#,data-annotations,implicit-conversion,explicit-conversion,C#,Data Annotations,Implicit Conversion,Explicit Conversion,我们在域模型中使用了自定义本地化字符串类型。我们希望使用验证属性来装饰属性,如MaxLength。为此,我们添加了隐式运算符以启用此属性所需的强制转换 奇怪的是,操作符似乎从未被调用,并且在attributesIsValid方法中抛出了InvalidCastException get。在我们自己的项目工程中执行此强制转换 在这个系统clr ngen'ed属性或其他东西中是否有一个特殊的强制转换行为编译器magix // Custom type public class LocalizedStri

我们在域模型中使用了自定义本地化字符串类型。我们希望使用验证属性来装饰属性,如
MaxLength
。为此,我们添加了隐式运算符以启用此属性所需的强制转换

奇怪的是,操作符似乎从未被调用,并且在attributes
IsValid
方法中抛出了InvalidCastException get。在我们自己的项目工程中执行此强制转换

在这个系统clr ngen'ed属性或其他东西中是否有一个特殊的强制转换行为编译器magix

// Custom type
public class LocalizedString
{
    public string Value
    {
        get { return string.Empty; }
    }

    public static implicit operator Array(LocalizedString localizedString)
    {
        if (localizedString.Value == null)
        {
            return new char[0];
        }

        return localizedString.Value.ToCharArray();
    }
}

// Type: System.ComponentModel.DataAnnotations.MaxLengthAttribute
// Assembly: System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.ComponentModel.DataAnnotations.dll
public override bool IsValid(object value)
{
  this.EnsureLegalLengths();
  if (value == null)
  {
    return true;
  }
  else
  {
    string str = value as string;
    int num = str == null ? ((Array) value).Length : str.Length;
    if (-1 != this.Length)
      return num <= this.Length;
    else
      return true;
  }
}


[TestMethod]
public void CanCallIsValidWithLocalizedString()
{
    // Arrange
    var attribute = new MaxLengthAttribute(20);
    var localized = new LocalizedString { Value = "123456789012345678901" };

    // Act
    var valid = attribute.IsValid(localized);

    // Assert
    Assert.IsFalse(valid);
}
任何类型的运算符仅在编译时已知对象类型时才适用。它们不会“动态”应用于
对象

您可以尝试使用
dynamic
这样做

示例:

using System;

class Foo
{
    public static implicit operator Array(Foo foo)
    {
        return new int[0]; // doesn't matter
    }
    static void Main()
    {
        Foo foo = new Foo();
        Array x = (Array)foo; // implicit operator called via compiler
        dynamic dyn = foo;
        Array y = (Array)dyn; // implicit operator called via dynmic
        object obj = foo;
        Array z = (Array)obj; // implicit operator NOT called
                              // - this is a type-check (BOOM!)
    }
}
你写

((Array) value)
但是静态类型的值是object。因此,这被编译为从对象到数组的转换。您的转换运算符从未被考虑过

换成

((Array)(value as LocalizedString))

你会没事的。

+1我不得不读了两遍,直到我看到“这是一个类型检查”!因此,从未知类型的对象转换为其他对象的唯一方法是使用IConvertible?@Adriano我不确定我是否会过于倾向于
IConvertible
,但事实上是这样的,是的,在这种情况下,我们可能必须创建自己的自定义LocalizedMaxLength属性来处理LocalizedString?还有其他方法吗?遇到了这个问题。我的反序列化例程返回一个Int64Proxy对象,当从动态ExpandObject字段读取时,会调用其隐式Int64运算符。但是,由于ExpandoObject在不存在时尝试访问该值时会抛出错误,因此必须首先将ExpandoObject强制转换为IDictionary并调用TryGetValue,该函数返回一个“object”。尽管对象的基础类型是包含隐式强制转换运算符的Int64Proxy,但这种普通“对象”类型不能隐式强制转换。所以,是的,动态工作。如果字段不存在,ExpandoObject究竟为什么会抛出错误。它是一个动态对象,应该有一个明确的值,如Flash/AS3的“undefined”,以区别于现有的空值。由于客户端可能包含也可能不包含我要查找的值(这是可选的),我不能只写
,如果(data.optionalField!=未定义)
,我必须写
对象字段值;if((数据为IDictionary).TryGetValue(“optionalField”,out fieldValue))
等,除非LocalizedString是定义隐式转换运算符的私有类。我使用一个私有Int64代理类,该类是反序列化过程的结果(JSON.NET无法从两部分高/低值反序列化为值类型结构,如Int64,因此我必须创建一个引用类型代理类,该类可以通过高/低部分的两个单独赋值进行初始化)。从动态对象读取时,隐式运算符可以工作,但如果我必须通过IDictionary测试其存在性,则生成的对象无法强制转换为Int64Proxy,因为它是一个私有类。只有“动态”对象才能正确查找并调用我的Int64Proxy类上的隐式运算符,因此,由于ExpandooObject在请求不存在的字段值时会抛出错误,而不是返回undefined或null,因此我不得不使用
IDictionary
接口尝试获取一个普通的旧对象值,除非我首先强制转换到Int64Proxy,否则无法调用其隐式运算符,因为它是一个私有类。
((Array)(value as LocalizedString))