【问题标题】:"Specified cast is not valid" error in TypeConverter.ConvertToTypeConverter.ConvertTo 中的“指定转换无效”错误
【发布时间】:2019-03-10 05:12:10
【问题描述】:

我正在为我的 winform 程序制作控件,保存测试表单时出现此错误。


这是解决方案结构,控件库分为一个项目,另一个是包含测试表单的测试项目。

Solution
├ Test (Test Project)
│ └ Form1
└ WinFormControls (Library Project)
  └ ImageButton (UserControl)

控件附有TypeConverter,这里是简要代码。

为了问题简单,我省略了其他方法,大家可以阅读this link

public class StateConverter : ExpandableObjectConverter
{
    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if(destinationType == typeof(InstanceDescriptor))
        {
            var ctor = typeof(State).GetConstructor(new Type[] { typeof(int), typeof(Image) });
            if (ctor != null)
            {
                var state = (State)value;
                return new InstanceDescriptor(ctor, new object[] { state.GetData(), state.Image });
            }
        }
        return base.ConvertTo(context, culture, value, destinationType);
    }
}

错误并不总是发生在这里是重现的步骤:

  1. 在VS中打开solution
  2. 打开 Form1 设计器,从工具箱中拖出一个 ImageButton
  3. 在 ImageButton.cs 中进行一些更改(例如,添加一个空格),然后重新生成解决方案
  4. 返回Form1设计器,在属性窗口中修改NormalState属性,然后保存,出现错误。
  5. 从那时起,如果您在 ImageButton 上进行更改并保存,即使从工具箱中拖动另一个 ImageButton,也会显示错误,除非重新打开 VS。

经过一些调试,我发现错误发生在这一行:

var state = (State)value;

一开始我猜这个值是空的,所以我添加了一些日志:

try {
    var state = (State)value;
} catch (Exception ex) {
    File.AppendAllText("errorlog.txt", ex.ToString() +
       (value == null ? "NULL" : value.GetType().ToString());
}

终于找到了:

System.InvalidCastException:指定的强制转换无效。在 WinFormControls.ImageButton.StateConverter.ConvertTo ...... WinFormControls.ImageButton+State

所以值不为空,值的类型正是我所转换的。


更新

输出 AssemblyQualifiedName IsAssignableFrom 是:

value.GetType().AssemblyQualifiedName;
typeof(State).AssemblyQualifiedName;
typeof(State).IsAssignableFrom(value.GetType());
value.GetType().IsAssignableFrom(typeof(State))
value is State
ReferenceEquals(value.GetType(), typeof(State))

奇怪的结果:

WinFormControls.ImageButton+State, WinFormControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
WinFormControls.ImageButton+State,WinFormControls,版本=1.0.0.0,文化=中性,PublicKeyToken=null
假的
假的
假的
假的

两个问题:

  1. 为什么会发生错误?
  2. 在开发过程中如何避免它?

【问题讨论】:

  • 看起来确实很奇怪...你确定 (value == null || typeof(State) == value.GetType()) 吗?
  • 这很痛苦,但我想你需要关闭VS并清除设计器程序集缓存。看看this post。您需要考虑的另一件事是在ConvertTo 中检查if (value is State),但这不能解决问题,但通常在类型转换器中需要。
  • @EricMcLachlan 看到捕获代码,我确定值不为空(状态是一个结构,所以理论上它也不会为空)并且我的问题的更新,我尝试输出两种类型的AssemblyQualifiedName,它们是相同的。
  • @EricMcLachlan 设计器中类和结构之间的行为几乎不同,很难改变,但可以选择。 typeof(value.GetType()).IsAssignableFrom(State) 也是假的,好像这两种完全不一样,可能是缓存问题。
  • 感谢@RezaAghaei 检查is State 很有帮助。

标签: c# winforms casting windows-forms-designer typeconverter


【解决方案1】:

这是因为类型转换器和设计器加载类型的方式不同。类型转换器使用 CLR 缓存加载类型,而设计器使用类型解析服务。因此,当您更改类库项目并再次构建它时,CLR 从之前缓存的 CLR 中的类型版本加载程序集,而设计器从新构建的程序集加载它,这会导致类型不匹配。

解决问题,在类库项目中:

  1. 打开AssemblyInfo.CS 并更改程序集版本属性以更改每个构建中程序集的内部版本号并保存文件。这会强制 CLR 使其缓存无效并从新版本的程序集加载新类型:

    [assembly: AssemblyVersion("1.0.0.*")]

  1. 右键卸载类库项目,然后右键编辑项目文件,将项目确定性构建改为false:

    <Deterministic>false</Deterministic>

问题已经解决in this forum post

【讨论】:

  • 我假设你已经在ConvertTo中申请了if (value is State)
  • 在应用解决方案之前,您可能需要关闭解决方案并清理 VS 设计器缓存,如我所述 here。仅供参考,我已将解决方案成功应用于您共享的项目。
  • 如果出于任何原因需要调试设计器,请查看this post
  • 你好@Reza Aghaei。你真的又是我的男人了。感谢一位上帝,你在这里。请问我需要知道Visual Studio默认情况下这个问题吗?我花了很长时间,会撞到墙上……第一次 TypeConverter 工作得很棒(转换器是你帖子中的一个……)但是当第二次修改它时。第三,等它在重新启动视觉工作室之前永远不会工作。那么这是添加确定性的唯一方法吗?构建或...时是否有任何副作用、性能问题...
  • 嗨@deveton。我不太确定可能的副作用,但解决此问题的另一种可能方法是将类型转换器放在与用户控件不同的程序集中。
猜你喜欢
  • 1970-01-01
  • 2018-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 2013-08-27
  • 2015-01-06
  • 1970-01-01
相关资源
最近更新 更多