Эффективное определение типа

#c# #algorithm #type-conversion

#c# #алгоритм #преобразование типов

Вопрос:

Я столкнулся с перспективой использования фабричного метода, подобного приведенному ниже, для создания DI на основе setter (потому что Entity Framework не позволяет мне использовать DI на основе конструктора). Поэтому, когда объект материализуется в EF, я вызываю метод ниже, чтобы выполнить настройку DI.

 public void AttachKeyHolder(ILocalizableEntity localizableEntity)
        {
            var ft = (localizableEntity as FeeType);
            if (ft != null)
            {
                localizableEntity.KeyHolder = new FeeTypeKeyHolder();
                return;
            }

            var other = (localizableEntity as OtherType);
            if (other != null)
            {
                localizableEntity.KeyHolder = new OtherTypeKeyHolder();
                return;
            }

            // And on and on and on for every applicable type            
        }
 

Мне это не нравится, потому что это становится проблемой на основе n, при которой, если у меня есть 20 типов, которым нужна эта инъекция установщика, то какой бы из них ни был проверен на 20-м месте, требуется в 20 раз больше времени для проверки в качестве первой проверки типа, и поскольку я делаю это каждый раз, когда объект материализуется в Если он, скорее всего, не будет масштабироваться.

Итак, я ищу лучший алгоритм. Предыдущее «решение» состояло в том, чтобы просто назначить соответствующий держатель ключа в конструкторе связанного объекта, как показано ниже:

 public FeeType()
{
    this.KeyHolder = new FeeTypeKeyHolder();
}
 

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

Ответ №1:

Вы можете пометить свойства, которые вам нужно установить через DI, своим атрибутом, например [Inject] , а затем в нужном месте (например, конструктор) вызвать вспомогательный метод like MyHelper.InjectProperties(this) . В помощнике вы можете получать атрибуты с помощью [Inject] и разрешать их значения непосредственно из конструктора.

Большинство фреймворков IoC / DI поддерживают внедрение свойств эффективным по производительности способом, поэтому в лучшем случае вам не нужно будет реализовывать его самостоятельно.

Комментарии:

1. Я не могу понять, как это мне помогает. Не могли бы вы объяснить немного подробнее? Если я использую MyHelper. InjectProperties, я скрываю зависимость (на этот раз от MyHelper), и мне все равно придется выполнять поиск типа в InjectProperties …. на этот раз с дополнительным снижением производительности при использовании отражения для извлечения приписываемых свойств. Рад признать, что я, возможно, что-то здесь упускаю.

2. В реальной жизни у вас действительно строгий набор типов, поэтому вы можете создать кэш времени выполнения для Type -> Inject properties . Вместо this.Prop = new MyType() того, чтобы я советую разрешать конкретный тип непосредственно из контейнера — таким образом, вы будете использовать один и тот же контейнер для всех зависимостей. MyHelper здесь является «лучшей» зависимостью, поскольку контейнер по-прежнему владеет созданием определенных экземпляров — в вашем подходе вы скрываете зависимости И сами создаете эти зависимости, что я считаю хуже с точки зрения дизайна (продолжение)

3. И опять же, внедрение атрибутов должно поддерживаться в большинстве фреймворков. Если вы не являетесь владельцем создания объекта, единственный способ заполнить зависимости — это изнутри объекта, поэтому вам придется сделать что-то вроде MyHelper. Впрыскивать изнутри объекта.

Ответ №2:

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

В любом случае, вы можете использовать Visitor patter для реализации двойной отправки, поэтому вместо 20 тестов для типа объекта вы вызываете 2 виртуальных метода, то есть два поиска в таблицах виртуальных методов, что может быть быстрее, чем 20 тестов, однако есть накладные расходы на два косвенных вызова функций, выделение фрейма стекадля каждого из них и т.д. Но опять же, действительно ли мы хотим углубляться так глубоко в случае проекта C # с Entity Framework?

Пример:

 class Visitor {
   public virtual void Visit(FeeType f) {}
   public virtual void Visit(OtherType t) {}
}

class abstract BaseType { public abstract void Accept(Visitor v); }

class FeeType : BaseType {
   public override void Accept(Visitor v) { v.Visit(this); }
}

class OtherType : BaseType {
   public override void Accept(Visitor v) { v.Visit(this); }
}

class DIVisitor : Visitor {
   public virtual void Visit(FeeType f) { f.KeyHolder = new ... }
   public virtual void Visit(OtherType t) { t.KeyHolder = new ... }
}

public void AttachKeyHolder(ILocalizableEntity localizableEntity)
{
   var ft = (localizableEntity as TypeBase);
   ft.Accept(new DIVisitor());
}
 

Вы также можете реализовать поиск правильного метода на основе типа с помощью хеширования.

 HashMap<Type, Action<BaseType>> actions;
actions.Add(typeof(FeeType), x => { ((FreeType)x).KeyHolder = new .... });
...
actions[ft.GetType()].Invoke(ft);
 

Комментарии:

1. 1 Для другого взгляда на это. Как вы говорите, это выполнимо, но, возможно, немного СТРАННО.