2009-07-07 6 views
1

Я написал резольвер, поэтому у меня может быть очень примитивная структура DI. В рамках я допускаю, чтобы зависимый преобразователь указывал, какие типы по умолчанию загружаются, если ничего не указано или не зарегистрировано.Каков наилучший способ решения объекта?

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

Пример:

T LoadDefaultForType<T>() 
{ 
    T _result = default(T); 

    if (typeof(T) == typeof(ISomeThing) 
    { 
     result = new SomeThing(); 
    } 
    ... more of the same 
    else 
    { 
     throw new NotSupportException("Give me something I can work with!"); 
    } 

    return _result; 
} 

Update

Использование этого было бы получить объект по умолчанию для данного интерфейса в том случае, если модуль или узел не сконфигурирован интерфейс с бетонного типа.

Так, например:

IoC.Resolve<ISomeThing>(); 

должен был бы вернуться ко мне объект-то, если ничего другого не было зарегистрировано в ISomeThing. LoadDefaultForType в этом случае является последним попыткой использовать по умолчанию (в этом случае независимо от моей модели домена).

Решимость может пролить некоторый свет на это, а также:

T Resolve<T>() 
{ 
    T _result = default(T); 
    if (ContainedObjects.ContainsKey(typeof(T)) 
     _result = (T)ContainedObjects[typeof(T)]; 
    else 
     _result = LoadDefaultForType<T>(); 
    return _result; 
} 

Есть мысли? Есть ли лучший способ загрузить типы по умолчанию, учитывая, что я пытаюсь разрешить подход, основанный на соглашениях о переходе?

ответ

2

Несколько предложений:

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

В качестве альтернативы вы можете использовать отражение для поиска доступных (загруженных) сборок или конкретного типа, который реализует интерфейс. Это может быть медленный и дорогостоящий процесс, поэтому имеет смысл только в том случае, если случай по умолчанию встречается редко.

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

+0

Мне очень нравится ваша идея атрибута. Я думаю, что это может быть дорога. – Joseph

+0

И с соглашением об именовании, которое хорошо привязалось бы к «конвенции/конфигурации». – GalacticCowboy

2
public T LoadDefaultForType<T>() 
     where T : new() 
    { 
     T _result = new T(); 

     return _result; 
    } 

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

Предлагаю взглянуть на Unity для динамической загрузки типов, т.е. Зависимость впрыска

+0

@Neil Спасибо, я изменю свой вопрос, чтобы лучше проиллюстрировать. Я бы определенно использовал рамки DI, если мог, но в этом случае у меня нет выбора, кроме как сворачивать свои собственные. – Joseph

+0

@Neil Кроме того, я не могу использовать новое ограничение, потому что T должен поддерживать интерфейсы, которые будут типичным использованием. – Joseph

+0

где T: IInterface, new() , но это означает, что LoadDefaultType может поддерживать только типы, которые выводятся из IInterface, работает ли это? – Neil

1

Подход Нейла является лучшим, если T можно решить (я думаю, он также должен быть в той же сборке?).

В вашем классе вы можете создать собственный «реестр», который может быть использован с System.Reflection для создания экземпляров без инструкции гигантского переключателя. Это сохраняет вашу «условную конфигурацию», а также удерживает вас DRY.

Редактировать

В сочетании с одним из аспектов ответа LBushkin, чтобы показать некоторые рабочий код. (По крайней мере, он компилирует в моей голове, и взят из примера, что я знаю, работы ...)

public T LoadDefaultForType<T>() 
{ 
    try 
    { 
    string interfaceName = typeof(T).AssemblyQualifiedName; 

    // Assumes that class has same name as interface, without leading I, and 
    // is in ..."Classes" namespace instead of ..."Interfaces" 
    string className = interfaceName.Replace("Interfaces.I", "Classes."); 

    Type t = Type.GetType(className, true, true); 
    System.Reflection.ConstructorInfo info = t.GetConstructor(Type.EmptyTypes); 

    return (T)(info.Invoke(null)); 
    } 
    catch 
    { 
    throw new NotSupportException("Give me something I can work with!"); 
    } 
} 

Обратите внимание, что - как написано - он не будет работать через границы сборки. Однако это может быть сделано с использованием, по существу, одного и того же кода - вам просто нужно указать имя, присвоенное сборке, методу Type.GetType(). (фиксированный - используется AssemblyQualifiedName вместо FullName; предполагается, что интерфейс и реализующий класс находятся в одной и той же сборке.)

+0

Если я правильно понимаю, решение Neil не соответствует счету, потому что оно не позволяет переопределить тип по умолчанию. –

+0

@GalacticCowboy Подход Нила на самом деле не решает мою проблему. Я уточнил свой вопрос, чтобы лучше объяснить. То, что вы описываете, - это именно то, что я пытаюсь сделать, но я не знаю, как лучше создать «реестр», не создавая гигантскую структуру if else или switch. Я не уверен, как я буду использовать размышления, чтобы решить эту проблему, но я открыт для идей. Примером чего-то было бы действительно полезно. – Joseph

0

Одним из способов было бы иметь список пар AssemblyCualifiedName, причем первый из них содержит базовый тип, а второй - дочерний, который должен быть надуман. Это может быть в вашем App.config или XML-файле или что-то еще удобное. Прочитайте этот список во время запуска и используйте его для заполнения Словаря, который будет использоваться в качестве справочной таблицы. Для ключа вы можете использовать AssemblyQualifiedName базы или, возможно, соответствующий экземпляр Type. Для значения вы должны, вероятно, рассмотреть возможность получения ConstructorInfo для дочернего типа.

+0

Я с вами, но это Конфигурация, а не Конвенция, а мой метод LoadDefaultForType - о создании моей Конвенции. У меня есть другие механизмы в моей инфраструктуре DI для обработки конфигурации уже. Часть Конвенции (эта часть) - вот что мне интересно. – Joseph

+0

Контрактная часть будет вызвана сбоем TryGet. В этот момент вы можете взять AQN и добавить стандартный суффикс, такой как «По умолчанию», и использовать Type.GetType, чтобы просмотреть его, добавив в таблицу ConstuctorInfo. Если это не удается, вы смотрите базовый тип, добавляя идентификационную запись, чтобы избежать повторения всей этой работы. Вам нужно будет заблокировать стол, делая это, или, по крайней мере, в течение нескольких мгновений вам нужно получить к нему доступ. –

0

Лучшим способом загрузки типов по умолчанию является предоставление метода добавления, хранящего этот тип в хеш-таблице. Это отделяет контейнер зависимостей от логики регистрации.

Вы можете выбрать позже, чтобы изменить регистрационный код, чтобы прочитать типы из какого-либо файла или что-то в этом роде.

+0

У меня есть метод Register в моей структуре DI.Любой может зарегистрировать объект, связанный с ним интерфейсом, но это то, что я рассматриваю как часть конфигурации структуры. Мне нужно иметь план резервного копирования, когда кто-то выбирает не настраивать интерфейсы. – Joseph

Смежные вопросы