Подтвердить что ты не робот

Можно ли добавить интерфейс к существующим типам .NET?

В моем примере ниже представлены 2 класса NET, которые содержат метод CommonMethod. Я хотел бы спроектировать MyMethod, который может принять любой класс (использование), сохраняя при этом функции, общие для NetClassA и NetClassB. Case1 будет делать именно это, только это незаконно, как указано ниже. Case2 также достигнет цели, за исключением INetClassA и INetClassB, которые не существуют. Поэтому мой вопрос заключается в способе навязывания пользовательского интерфейса (ICommonNetMethods) для существующих типов .NET(Case 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

4b9b3361

Ответ 1

Есть способы решить это, что связано с творческим мышлением.

Самое очевидное:

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

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

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

Другие взгляды:

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

и/или

Отражение

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

             =====================
             = ICommonNetMethods =
             =====================
                       | (derive)
         |-------------------------------|
====================            ====================
= NetClassAAdapter =            = NetClassBAdapter =
====================            ====================
         | uses (not derive)             | uses (not derive)
   =============                   =============
   = NetClassA =                   = NetClassB =
   =============                   =============

Ответ 2

Используйте 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

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

Если вы смотрите на более крупные интерфейсы, рассмотрите, как уже упоминалось, шаблон адаптера. Если идея обертывания каждого из ваших объектов со своими конкретными классами адаптера кажется слишком много раздутой для вашего доллара, то снова 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); }
   ...
}

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

var adaptA = new FuncImplementation { Func_f1 = MyF1, Func_f2 = Myf2 };
adaptA.f1();

Ответ 3

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

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

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

Ответ 4

Единственный способ, которым я могу думать (с моей точки зрения), - это получить от рассматриваемого класса .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;
     }
}

Ответ 5

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

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

Ответ 6

В С# 4.0 введено ключевое слово dynamic, которое позволяет разработчикам С# использовать duck typing (альтернатива шаблон адаптера). С его помощью вы можете определить 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, который не принимал никаких параметров, программа скомпилировалась, но сбой во время выполнения.

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