Почему этот TypeConverter не работает?
Я пытаюсь понять, почему приведенный ниже код работает не так, как ожидалось; TypeDescriptor
просто не подбирает пользовательский конвертер из атрибутов. Я могу только предположить, что совершил явную ошибку, но я не вижу этого.
- edit - этот код работает, когда я запускаю его в консоли самостоятельно, я на самом деле вызываю конвертер из более сложного приложения и из другого пространства имен.
- отредактируйте - в качестве альтернативы, любые предложения о том, как я могу отлаживать TypeDescriptor, чтобы я мог видеть, что происходит, и тогда я, вероятно, могу ответить на это сам.
- edit - эта проблема почти наверняка связана с фрагментами, находящимися в разных сборках.
- edit - Похоже, что это не работает из-за некоторого причуда загрузки сборок динамически - этот код работает под плагиновой архитектурой.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;
namespace MyTest
{
public class TestTester
{
public static void Main(string[] args)
{
object v = TypeDescriptor.GetConverter(typeof(MyTest.Test)).ConvertFromInvariantString("Test");
}
}
public class TestConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return false;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string) || base.CanConvertFrom(context, sourceType))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(Test) || base.CanConvertTo(destinationType))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value.GetType() == typeof(string))
{
Test t = new Test();
t.TestMember = value as string;
return t;
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value.GetType() == typeof(Test))
{
return ((Test)value).TestMember;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
[TypeConverterAttribute(typeof(TestConverter))]
public struct Test
{
public string TestMember { get; set; }
}
}
Ответы
Ответ 1
У меня была и эта проблема, и обходной путь к проблеме заключается в том, чтобы подписаться на событие AssemblyResolve текущего домена приложения и разрешить сборку вручную.
Это далеко не хорошее решение, но, похоже, оно работает. Я понятия не имею, почему структура ведет себя таким образом. Я бы очень хотел найти менее хакерский способ решения этой проблемы.
public void DoMagic()
{
// NOTE: After this, you can use your typeconverter.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AppDomain domain = (AppDomain)sender;
foreach (Assembly asm in domain.GetAssemblies())
{
if (asm.FullName == args.Name)
{
return asm;
}
}
return null;
}
Ответ 2
Ответ на этот другой вопрос должен быть применим здесь. Это гораздо более простое решение, чем подписка на AssemblyResolve
.
Таким образом, идея состоит в том, чтобы установить атрибут TypeConverter
используя полное строковое имя класса преобразователя типов, вместо использования typeof
для предоставления имени класса.
Другими словами, вместо этого:
[TypeConverterAttribute(typeof(TestConverter))]
public struct Test
{
...
}
сделай это:
[TypeConverterAttribute("MyTest.TestConverter")]
public struct Test
{
...
}
Ответ 3
Я видел случаи, когда я не мог выбирать атрибуты для внутренних полей из других сборок. Не уверен, что это ошибка .NET, или если она исправлена.
Единственное, что я могу сказать о том, что в сложном сценарии у вас может не быть разрешения Reflection.
Ответ 4
Это немного поздно, но эта проблема возникла, когда я попросил TypeConverter, который находится в другой сборке, на которую прямо не ссылается исполняемая сборка.
Ответ 5
Мы также наблюдали это поведение в подключаемых системах, включая загрузку сборок из-за пределов папки приложения.
Корень всего зла является недостатком реализации TypeDescriptorAttribute
.
Атрибут имеет две перегрузки конструктора, одну для спецификации типа обычного текста (которая - не неожиданно - чистая магия во время выполнения) и одна для ссылки ранней привязки typeof()
. Если вы используете второй путь, что может пойти не так? Ну, на самом деле атрибут использует только первый путь. Реальная и правильная ссылка типа времени выполнения сглажена в открытый текст, и здесь есть драконы. Поэтому не нужно писать typeof()
- это всегда сценарий открытого текста и магии внутри.
Решение? Не идеальный, но в нашем случае мы потребляли только преобразования типов внутри нашей системы, поэтому мы выбрали ValueSerializerAttribute
. Это в основном способ WPF делать одни и те же вещи. Его реализация правильна в отношении перегрузки typeof()
.ctor
в том, что ей удается сохранить идентификатор раннего типа и всегда загружает правильный тип, как написано в коде.
Если вы хотите использовать системный (или WinForms) код для использования конвертера типов, это не поможет.