Почему этот TypeConverter не работает? - PullRequest
13 голосов
/ 01 сентября 2010

Я пытаюсь понять, почему приведенный ниже код не работает должным образом; TypeDescriptor просто не выбирает пользовательский конвертер из атрибутов. Я могу только предположить, что совершил очевидную ошибку, но не вижу ее.

- edit - этот код, кажется, работает, когда я запускаю его в консоли самостоятельно, на самом деле я вызываю конвертер из гораздо более сложного приложения и из другого пространства имен.

- изменить - альтернативно любые предложения о том, как я могу отладить TypeDescriptor, чтобы я мог видеть, что происходит, и тогда я, вероятно, смогу ответить на него сам.

- правка - эта проблема почти наверняка связана с деталями, находящимися в разных сборках.

- 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; }
    }
}

Ответы [ 5 ]

12 голосов
/ 13 августа 2011

У меня тоже была эта проблема, и ее можно обойти, подписавшись на событие 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;
}
3 голосов
/ 07 марта 2017

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

Итак, идея состоит в том, чтобы установить атрибут TypeConverter, используя полное строковое имя класса преобразователя типов, а не typeofукажите имя класса.

Другими словами, вместо этого:

[TypeConverterAttribute(typeof(TestConverter))]
public struct Test
{
    ...
}

сделайте это:

[TypeConverterAttribute("MyTest.TestConverter")]
public struct Test
{
    ...
}
2 голосов
/ 05 февраля 2013

Это немного поздно, но эта проблема появилась, когда я попросил TypeConverter, который находится в другой сборке, на которую не ссылается исполняемая сборка.

1 голос
/ 01 сентября 2010

Я видел случаи, когда я не мог подобрать атрибуты во внутренних полях из других сборок. Не уверен, что это ошибка .NET или она была исправлена.

Единственное, что я могу сказать, это то, что в сложном сценарии у вас может не быть разрешения на Отражение.

0 голосов
/ 26 августа 2013

Мы также наблюдали такое поведение в подключаемых системах, включающих загрузку сборок извне папки appbase.

Корень всего зла - недостаток в реализации TypeDescriptorAttribute.

Атрибут имеет две перегрузки конструктора, один для спецификации типа открытого текста (что не случайно - чистая магия во время выполнения) и один для ранней ссылки typeof().Если вы используете второй путь, что может пойти не так?Ну, на самом деле атрибут использует только первый путь.Реальная и правильная ссылка на тип во время выполнения сведена в открытый текст, и здесь есть драконы.Так что бесполезно писать typeof() - это всегда сценарий с открытым текстом и магией внутри.

Решение?Не идеальный вариант, но в нашем случае мы потребляли преобразования типов только внутри нашей системы, поэтому вместо этого мы выбрали ValueSerializerAttribute.Который является в основном способом WPF делать то же самое.Его реализация верна в отношении перегрузки typeof() .ctor, так как она успешно сохраняет идентификатор типа с ранней привязкой и всегда загружает правильный тип, как написано в коде.

Если вам нужна система (или WinForms) код для использования преобразователя типов, это не поможет.

...