«Указанное приведение неверно» в TypeConverter.ConvertTo - PullRequest
0 голосов
/ 10 марта 2019

Я создаю элементы управления для моей программы winform, и эта ошибка возникает при сохранении тестовой формы.

error


Это структура решения, библиотека элементов управления разделена на 1 проект, а другой - тестовый проект, содержащий тестовую форму.

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

К элементу управления прикреплено TypeConverter, вот краткий код.

Чтобы упростить вопрос, я опускаю другие методы, вы можете прочитать его по по этой ссылке

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
  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 + Государство

Значит, значение не равно нулю, и тип значения в точности соответствует тому, что я приведу.


Update

Output 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, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = ноль
WinFormControls.ImageButton + State, WinFormControls, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = ноль
Ложь
Ложь
Ложь
Ложные

Два вопроса:

  1. Почему происходит ошибка?
  2. Как мне избежать этого во время разработки?

1 Ответ

1 голос
/ 10 марта 2019

Это связано с тем, что преобразователь и разработчик типов по-разному загружают тип. Преобразователь типов использует CLR-кеш для загрузки типа, а дизайнер использует службу разрешения типов. Поэтому, когда вы изменяете проект библиотеки классов и строите его снова, CLR загружает сборку из ранее кэшированной версии типа в CLR, в то время как конструктор загружает ее из вновь созданной сборки, и это приводит к несовпадению типов.

Чтобы решить проблему, в проекте библиотеки классов:

  1. Откройте AssemblyInfo.CS и измените атрибут версии сборки, чтобы изменить номер сборки сборки в каждой сборке и сохранить файл. Это заставляет CLR аннулировать свой кеш и загружать новый тип из новой версии сборки:

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

  1. Щелкните правой кнопкой мыши и выгрузите проект библиотеки классов, затем щелкните правой кнопкой мыши и отредактируйте файл проекта и измените детерминированную сборку проекта на false:

    <Deterministic>false</Deterministic>

Проблема уже решена в этом сообщении на форуме .

...