Можно ли добавить интерфейс к существующим типам .NET? - PullRequest
17 голосов
/ 03 августа 2011

Мой пример ниже включает 2 класса NET, которые оба содержат метод CommonMethod.Я хотел бы разработать MyMethod, который может принимать любой класс (Using), сохраняя при этом функциональность, общую для NetClassA и NetClassB.Case1 сделал бы только то, что только это незаконно, как указано ниже.Case2 также достиг бы цели, за исключением того, что INetClassA и INetClassB не существуют.Поэтому у меня вопрос, есть ли способ навязать пользовательский интерфейс (ICommonNetMethods) на существующие типы .NET (случай 3)?Приветствуются альтернативные решения моей проблемы.

// Case 1:  Illegal because "where" can only have 1 base class
public void MyMethod<Ttype>(Ttype myClass) where Ttype : NetClassA, NetClassB {}

// Case 2:  Legal to utlize multiple "where" interface types
public void MyMethod<Ttype>(Ttype myClass) where Ttype : INetClassA, INetClassB {}

// Case 3:  For this to work ICommonNetMethods must be added to NetClassA/NetClassB
public void MyMethod<Ttype>(Ttype myClass) where Ttype : ICommonNetMethods {}

NetClassA() { This .NET class has method CommonMethod() }
NetClassB() { This .NET class has method CommonMethod() }

interface ICommonNetMethods { void CommonMethod() }

Спасибо, aidesigner

Ответы [ 6 ]

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

Существуют способы решения этой проблемы, требующие творческого мышления.

Наиболее очевидное:

Шаблон адаптера

Вы создаете свой интерфейс, затем дваадаптеры, где каждый принимает NetClassA, а другой NetClassB.Ваш общий код остается общим и специфическим для адаптеров.

Это работает даже для закрытых классов.Вы не платите за услуги NetClassA или NetClassB.Я хотел бы оставить это для вас, чтобы выяснить реализацию, вернитесь через день, если вы хотите реализацию кода, я опубликую его.

Другие вещи, на которые стоит посмотреть:

Методы расширения

и / или

Отражение

Дополнительная справка

             =====================
             = ICommonNetMethods =
             =====================
                       | (derive)
         |-------------------------------|
====================            ====================
= NetClassAAdapter =            = NetClassBAdapter =
====================            ====================
         | uses (not derive)             | uses (not derive)
   =============                   =============
   = NetClassA =                   = NetClassB =
   =============                   =============
8 голосов
/ 04 августа 2011

Использование Func<>:

Предположим, что два класса, A и B, каждый с функцией Foo (хотя это не является обязательным требованием для этого решения, соблюдайте класс C ниже):

public class A { int Foo() { return 1; } }
public class B { int Foo() { return 2; } }
public class C { int Deviant() { return 3; } }

Затем в некотором фрагменте кода вы напишите:

var a = new A();
var b = new B();
var c = new C();
var fs = new Func<int>[] {() => a.Foo(), () => b.Foo(), () => c.Deviant()};

Итак, чтобы использовать это:

foreach(var func in fs) 
   Console.WriteLine(func());

, что в свою очередь выдаст:

1
2
3

Лямбда-функции имеют большое значение в C # и являются отличной технологией для изучения.Если вы незнакомы и хотели бы узнать больше, начните со страницы справки Microsoft .

Если вы рассматриваете более крупные интерфейсы, рассмотрите, как уже упоминалось, схему адаптера.Если идея обернуть каждый из ваших объектов в свои конкретные классы адаптеров кажется слишком большой для вашего бакса, то опять же, Func <> на помощь.

public interface ISomeInterface
{
   void f1();
   int f2(string p1);
   ...
}

public class FuncImplementation : ISomeInterface
{
   public Action Func_f1 { get; set; }
   public Func<string,int> Func_f2 { get; set; }
   ...
   void f1() { Func_f1(); }
   int f2(string p1) { return Func_f2(p1); }
   ...
}

Теперь вы можете сделать новые адаптеры встроенными:

var adaptA = new FuncImplementation { Func_f1 = MyF1, Func_f2 = Myf2 };
adaptA.f1();
4 голосов
/ 03 августа 2011

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

Вместо этого рассмотрим следующие варианты:

  1. Если у вас просто один метод в интерфейсе, вы можете использовать Вместо этого делегат.
  2. Вы можете создать простой класс-обертку для каждого из ваших типов и реализовать там интерфейс.
3 голосов
/ 03 августа 2011

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

Почему бы просто не проверить тип Ttype, который есть в методе, и выполнить свой код соответственно на основе типа?

Например:

public void MyMethod<Ttype>(Ttype myClass)
{

    string className = typeof(Ttype).Name;

    switch (className)
    {
        case "NetClassA":
            // Do stuff
            break;
        case "NetClassB":
            // Do stuff
            break;
        default:
            // Do something if necessary
            break;
     }
}
1 голос
/ 27 апреля 2017

В C # 4.0 введено ключевое слово dynamic, которое позволяет разработчикам на C # использовать утка, набирающую (альтернатива шаблону адаптера ). С его помощью вы можете определить MyMethod так:

public void MyMethod(dynamic myClass)
{
    myClass.CommonMethod();
}

Затем можно просто передать экземпляры NetClassA и NetClassB на MyMethod следующим образом:

var a = new NetClassA();
var b = new NetClassB();
MyMethod(a);
MyMethod(b);

Недостатком этого подхода является отсутствие статической проверки типов. Если в NetClassA или NetClassB не было метода с именем CommonMethod, который не принимал бы параметров, программа компилировалась, но не выполнялась во время выполнения.

Кроме того, поскольку нет связанного интерфейса, неясно, какие функции и свойства доступны. Избегайте использования этого подхода в общедоступных сборках.

1 голос
/ 06 августа 2011

Спасибо всем, меня по-настоящему поразили различные варианты.Сначала я уже начал использовать опцию делегата ( Использование параметров вложенного типа и рекурсии (C #) ) и имею почти идеальное решение.Второй пост в этой теме показывает мою точную реализацию.Этот подход пытается решить проблему, передавая только необходимую функцию «Добавить» NETClassA (SrgsItem) и NetClassB (SrgsElement) вместо всего класса.Это почти идеально, за исключением того, что в C # отсутствует поддержка «Generics Variance».

Что касается других опций, они все очень проницательны.После изучения темы делегата я буду пробовать подход Adapter / Func, предложенный Майклом и Эндрю (добавим комментарии).Если у вас есть время, пожалуйста, следуйте приведенной выше ветке делегата, поскольку это может помочь, и это может помочь понять другой аспект C #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...