2009-12-04 3 views
4

Я знаю, что C# не предлагает множественное наследование. И я знаю, что есть обходные пути, например, this one.Множественное наследование в C# - снова

Но вот проблема, с которой я столкнулся сегодня, не может изобразить ни одного ELEGANT обходной путь. Я добавлю какой-нибудь абстрактный код-образец, чтобы вы быстрее его получили ...

(пусть это будет реальный код ASP.NET, потому что эти кодовые образцы класса A, класса B действительно запутывают) :

public class AdminPage : System.Web.UI.Page 
{ 
    protected override void OnInit(EventArgs e) 
    { 
     //if not an admin - get out 
     if(!CurrentUserIsAdmin()) Response.End(); 

     base.OnInit (e); 
    } 
} 

public class JQueryPage : System.Web.UI.Page 
{ 
    protected override void OnInit(EventArgs e) 
    { 
     RegisterJQueryScript(); 
     base.OnLoad (e); 
    } 
} 

//now here's what I REALLY miss in C# 
public class AdminJQueryPage : AdminPage, JQueryPage; 

ответ

9

Вывести функциональность? Это лучше для одиночной ответственности. Вы должны тщательно подумать о своих конструкторах.

interface IAdminPage { 
    public string AdminPageMethod(); 
} 

interface IJQueryPage { 
    public string JQueryPageMethod(); 
} 

internal class AdminPage : IAdminpage { 
    private string someString; 

    internal AdminPage(string value) { 
    this.someString = value; 
    } 

    public string AdminPageMethod() { 
    return "AdminPage result with some string: " + this.someString; 
    } 
} 

internal JQueryPage : IJQueryPage { 
    private int someNumber; 

    internal JQueryPage(int value) { 
    this.someNumber = value; 
    } 

    public string JQueryPageMethod() { 
    return "JQueryPage result with number: " + this.someNumber; 
    } 
} 

class AdminJQueryPage : IQueryPage, IAdminpage { 
    private readonly IAdminPage adminPage; 
    private readonly IJQueryPage jqueryPage; 

    public AdminJQueryPage(string someString, int someNumber) { 
    this.adminPage = new AdminPage(someString); 
    this.jqueryPage = new JQueryPage(someNumber); 
    } 

    public string AdminPageMethod() { 
    return this.adminPage.AdminPageMethod(); 
    } 

    public string JQueryPageMethod() { 
    return this.adminPage.JQueryPageMethod(); 
    } 
} 

Если вы действительно хотите, множественное наследование, посмотрите на traits

Редактировать Скала: добавлены Попутно значений конструктора, состоящих из классов. Также сделали классы внутренними (не могут быть доступны или созданы вне сборки), потому что они только когда-либо создаются классом AdminJQueryPage, который является классом с открытым доступом.

+0

Композиция элегантна и является лучшей альтернативой множественному наследованию во многих случаях. – Quibblesome

+0

Дело в том, что один класс drived переопределяет метод A, другой производный класс переопределяет метод B. Таким образом, этот ответ не помогает. – Alex

+0

Да, но вопрос в том, действительно ли это вам нужно? Или вы могли бы решить проблему лучше, разложив проблему на отдельные объекты? Часто на самом деле не требуются чрезмерные и глубокие иерархии наследования. Почему бы просто не решить проблему необходимости нового класса с совершенно новым классом «IAdminJQueryPage», составляющим совершенно новый (или, возможно, полученный) «IAdminPage» и «IJQueryPage»? – Joe

2

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

+0

+1, важная техника –

3

Я тоже пришел из C++ и не пропустил его, особенно начиная с чтения Рефакторинг [и используя для этого инструмент, отличный от OOTB].

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

Или вы можете Extract Method код в вспомогательные классы и называем это (то есть, пример Джо)

Или вы можете положить помощников в одном базовом классе и вызвать из этого.

В любом случае ваш код будет более четким.

Это только вопрос времени, прежде чем ваши микшины начнут перекрываться, а затем ваш общий набор методов для управления этой сложностью должен быть задействован - на C++, MI должен был быть только одним инструментом в наборе, а не очень сексуальный молот.

1

Самый простой подход заключается в построении иерархии - позволить AdminPage наследовать от JQueryPage так:

public class AdminPage : JQueryPage 
{ 
    protected override void OnInit(EventArgs e) 
    { 
     //if not an admin - get out 
     if(!CurrentUserIsAdmin()) Response.End(); 

     base.OnInit (e); 
    } 
} 

public class JQueryPage : System.Web.UI.Page 
{ 
    protected override void OnLoad(EventArgs e) 
    { 
     RegisterJQueryScript(); 
     base.OnLoad (e); 
    } 
} 

//now here's what I REALLY miss in C# 
public class AdminJQueryPage : AdminPage 

Я думаю, некоторые из этой неловкости исходит из модели страницы ASP.NET, которая использует перекрытый базовый класс методы.

+0

Проблема в том, что MI (и mixins) позволяет произвольно выбирать, с какими услугами вы хотите перемешать. Он работает с наборами. Но если это работает в контексте вопросников, это obviosuly чистой ans ямочки. –

0

Вы можете сделать это с помощью интерфейсов

public interface IJQueryPage 
    { 
    } 

    public abstract class AdminPage : System.Web.UI.Page 
    { 
     protected override void OnInit(EventArgs e) 
     { 
      //if not an admin - get out 
      if(!CurrentUserIsAdmin()) Response.End(); 
      base.OnInit (e); 
     } 
     protected override void OnLoad(EventArgs e) 
     { 
      if (this is IJQueryPage) 
      { 
       RegisterJQueryScript(); 
      }    

      base.OnLoad (e); 
     } 
    } 

    public class AdminJQueryPage : AdminPage, IJQueryPage 
    { 
    } 
+0

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

+0

Атрибуты обычно предпочтительны для интерфейсов маркеров. Но если вы действительно захотите это сделать, вы должны сказать 'if (это IJQueryPage)' вместо 'if (this.GetType(). GetInterface (" IJQueryPage ")! = Null)'. Пример Джо намного чище, чем это, однако, закончится. –

+0

Рубен: да, ты прав. Вероятно, я слишком много размышляю :-) Я изменю свой ответ. –

2

Даже если бы это было возможно, одна проблема с семантикой МИ на основе решения конкретной проблемы, которую вы подняли, что происходит на стороне разметки? Метод Render(), который генерирует разметку, запускается сначала в одном классе, а затем в другом?Вероятно, это не то поведение, которое вы хотите, когда оба класса генерируют целые страницы.

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

  1. Page Адаптеры
  2. управления Адаптеры
  3. Пользовательские пользовательских элементов управления
  4. HttpModules
  5. Master Pages
  6. отображение тегов

Лучший выбор, конечно, зависит от детали вашего приложения. В случае, если это полезно, я рассказываю об этих вариантах в моей книге, включая пример кода: Ultra-Fast ASP.NET.

+0

ясно, что вы хотите виртуального MI здесь или посмотрите на него другим способом, которым вы просто хотите смешивать добавленное поведение и иметь одну базу System.Web.UI.Page –

+0

Конечно. Я предполагаю, что моя точка зрения была в том, что mixins! = MI, о чем спрашивал OP. – RickNZ

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