2013-04-03 2 views
5

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

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

Как обойти это? Можно ли повторно просмотреть ссылочный тип? Должен ли я действительно писать собственную оболочку, если я хочу точно такую ​​же функциональность? Или мне даже нужно снова ссылаться на низкоуровневый DLL?

(Если это помогает:

dll1: class myClass, myClass GetMyClass() 
dll2: myClass GetMyClass() 
exe: how to access result from calling GetMyClass (dll2) without referencing dll1? 

)

+0

Вам понадобится загрузить сборку, содержащую класс во время выполнения, чтобы вернуть класс. – Romoku

ответ

1

Невозможно достичь этого, если myClass определен в dll1, так как объекты типа myClass могут быть созданы во время выполнения. Чтобы этого избежать, вам необходимо изменить тип возврата GetMyClass() в dll2, чтобы вернуть что-то определенное в dll2. Это может быть класс, очень похожий на myClass и имеющий те же свойства (вы можете даже использовать такие инструменты, как AutoMapper, чтобы легко конвертировать между объектами), но это определенно должно быть в dll2. Что-то вроде:

// dll1 
class myClass 
{ 
    ... 
} 

myClass GetMyClass() 
{ 
    ... 
} 

// dll2 
class myClass2 
{ 
    public myClass2(myClass c) 
    { 
     // instantiate myClass2 with values from myClass 
    } 
} 

myClass2 GetMyClass() 
{ 
    // somehow get myClass and convert it to myClass2 here 
} 
+0

На самом деле, я пошел на аналогичное решение .. на данный момент (позже возможно рефакторинг) я выставляю функции из myClass как функции dll2 (каждый раз при получении объекта myClass). (В зависимости от того, сколько из них, наконец, произойдет, мне, возможно, придется изменить подход к чему-то более аккуратному.) –

1

Вызывающий должен иметь ссылку на класс в Dll1, чтобы знать, какой тип он обращается. Итак, да, вам нужно обратиться к первой dll в exe. Поскольку GetMyClass() возвращает тип в DLL1, тип должен быть раскрыт в exe, поэтому dll1 следует ссылаться.

1

Одним из решений здесь является предоставление 4-й DLL, содержащей интерфейсы для ваших классов. Вы бы указали это на всех трех ваших слоях и вернули эти интерфейсы вместо своих классов.

Это должно дать вам хорошее представление о том, что я имею в виду:

// DLL1 
class ClassInDLL1 : IClassInDLL1 
{ 
} 

// DLL2 
class ClassInDLL2 
{ 
    public IClassInDLL1 GetClassInDLL1() 
    { 
     return new ClassInDLL1(); 
    } 
} 

// DLL3 
class ClassInDLL3 
{ 
    public void DoSomething() 
    { 
     var dll2 = new ClassInDLL2(); 
     var dll1 = dll2.GetClassInDLL1(); // dll1 variable is of type IClassInDLL1 

     // do stuff with dll1 
    } 
} 

// interface DLL 
interface IClassInDLL1 
{ 
} 

Я буду честный, хотя, наслоение архитектуры, как это обычно не является удивительной идея, если ваш проект действительно большой. Я считаю, что искусственное создание сборок в сборке, подобных этому заблаговременно, может вызвать у вас ненужную боль, не говоря уже о том, что вы получаете 3-4 сборки для среднего или небольшого проекта, который может понадобиться только 1.

+0

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

3

Вам нужно для разделения всех общих классов, которые вы используете во всех ваших слоях, в новую dll, затем ссылайтесь на эту DLL на каждый проект.

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

// common dll 
public interface IMyClass 
{ 
    string MyData { get; set; } 
    IMyClass GetMyClass(); 
} 

// dll1 
public class myClass : IMyClass 
{ 
    public string MyData { get; set; } 
    public IMyClass GetMyClass() { return new myClass() { MyData = "abc" }; } 
} 

// dll2 
public class myClass2 
{ 
    public IMyClass GetMyClass() 
    { 
     var c1 = new myClass(); 
     var c2 = c1.GetMyClass(); 
     return c2; 
    } 
} 

// exe (references common and dll2) 
public class Program 
{ 
    public static void Main(string[] args) 
    { 
     var c1 = new myClass2(); 
     IMyClass c2 = c1.GetMyClass(); 
     Console.Writeline(c2.MyData); 
    } 
} 
+0

Отличное решение! Я забыл сказать, что я не могу изменить dll1, поскольку он является внешним. Тем не менее, я держу ваш проект в уме. Я уверен, что что-то подобное снова появится. –

+0

См. Также: [шаблон лестницы] (http://stackoverflow.com/questions/29259414/stairway-pattern-implementation) – dlf

2

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

некоторые из ключевых функций, которые вы должны будете являются:

Assembly.LoadFrom(path); //get the assembly as a local object 
Activator.CreateInstance(type); //create an instance of a type 
Assembly.GetType(string);//fetch a type by name from the assembly 

После того, как у вас есть тип, основное отражение даст вам довольно много любой другой кусок вам нужно.

Вот отрывок из моего местного кода:

asm = Assembly.LoadFrom(Path.Combine(Environment.CurrentDirectory, filePath)); 

Type[] types = asm.GetTypes(); 
for (var x = 0; x < types.Length; x++) 
{ 
    var interfaces = types[x].GetInterfaces(); 
    for (var y = 0; y < interfaces.Length; y++) 
    { 
     if (interfaces[y].Name.Equals("MyTypeName")) 
     { 
      isValidmod = true; 
      var p = (IMyType)Activator.CreateInstance(types[x]); 
          //Other stuff 
      } 
    } 

Также обратите внимание: это довольно старый код прямо сейчас. Он не был ни пересмотрен, ни рефакторирован в течение многих лет, поэтому его по существу современный, как и .NET 1.1, НО: Это иллюстрирует суть. Как загрузить тип из удаленной сборки, на которую не ссылаются локально.

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

+1

Главное, что вы помните, это то, что у вас DONT действительно есть определение типа локально, поэтому ВСЕ, что вы делаете с ним должно быть сделано через отражение ... Свойства, методы, _everything_. У нашего кода есть локальный интерфейс, который реализуются всеми нашими «модулями», так что у нас есть что-то, чтобы обрабатывать их как локально, не имея дело со всем кодом отражения. Модули, в свою очередь, напрямую ссылаются на интерфейс высокого уровня, чтобы они могли его реализовать. – Nevyn

+0

Спасибо! Хорошее решение. Тем не менее .. Я «динамически ссылаюсь» на dll1, что в итоге довольно похоже. (Мне, возможно, придется это сделать по другой причине: разные несовместимые версии dll с различными сигнатурами будут выборочно загружаться в зависимости от обстоятельств. О, dll1 является внешним компонентом, если вы задумываетесь о таких вещах ..) –

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