2017-01-21 18 views
1

Скажем, у меня есть класс Book:Как определить подпись функции, которая должна быть реализована экземплярами класса?

public class Book{ 
    public string Title {get; set;} 
} 

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

Что-то вроде этого:

public class Book{ // need to make this abstract? 
    public string Title {get; set;} 
    public abstract string Read(int pageNum); 
} 

// I want to define instances that define their own behavior... 
public static Book It => new Book(){ // can't create instance of abstract... 
    Title = "It", 
    Read... // what do I do here? 
} 

Мои основные проблемы являются:

  • Сохраняя вещи настолько простыми, насколько это возможно. Реализация интерфейсов в абстрактных классах работает, но это вызывает у меня проблемы с n * 2, поскольку я добавляю больше экземпляров.
  • Мне нужно будет добавить большое количество этих пользовательских функций - передача Funcs через конструкторы кажется громоздкой.
  • На практике - это используется для определения Tenant. Tenants определены в памяти, и имеет множество статических свойств как domain, name, adminEmail и т.д. Это наниматель специфических свойства ... но теперь я пытаюсь реализовать жилец конкретного поведения - как GetBooks или FilterUsers. Я бы хотел, чтобы реализация была настолько простой, насколько это было возможно по-человечески. Прямо сейчас у меня есть «Если TenantA, сделайте это, иначе, если tenantB, сделайте это ...», посыпанный по всему моему коду. Я пытаюсь консолидировать всю конкретную логику и детальную информацию в одном месте - на примерах класса Tenant.
  • Другие примеры специфического поведения Арендатора - у вас есть программное обеспечение форума SaaS. На домашней странице форума A вы найдете GetCoverPhoto, прочитав статический файл. На домашней странице Форума B вы найдете GetCoverPhoto, прочитав домашнюю страницу блога. В настоящее время я говорю: «Если форум А, сделай это, иначе, если форум В, сделай это». Это тип специфического для арендатора поведения, которое я хочу определить на объекте Tenant, а не в коде. В моей основной логике я не хочу использовать какой-либо код, специфичный для арендатора.

Есть ли простая функция/шаблон на языке C#, который достигнет этого?

+0

@AlexD Я пробовал это - я обновлю вопрос, где я боюсь. – SB2055

+0

Ах, ОК, 'abstract' здесь не применяется. Вы можете подумать о том, чтобы сделать 'Read' делегатом. – AlexD

+0

«Прохождение Funcs через конструкторы кажется громоздким» Действительно? 'Read = pageNumber => LoadPage (pageNumber)' неудобен? В нем точно указано, что вам нужно сказать: для этого экземпляра «Книга» вы загружаете страницу, вызывая метод LoadPage. Способ указания различного поведения чтения для каждого экземпляра книги заключается в том, чтобы * указать другое поведение чтения для каждого экземпляра книги. * –

ответ

5

Что говорит NineBerry, очень подходит.

Существует еще один способ выполнить то, что вы можете пожелать. Если вы хотите динамически внедрить реализацию метода чтения в книгу. Это можно увидеть как strategy pattern. И может быть сделано как интерфейсы, как на многих языках, но в простейшей форме на C# это может быть сделано делегатами.Пример:

public class Book{ 
    Func<int, string> readFunc; 
    public Book(Func<int, string> readFunc) 
    { 
    this.readFunc = readFunc; 
    } 
    public string Title {get; set;} 
    public string Read(int pageNum) { return readFunc(pageNum); } 
} 

Затем используйте его как:

public static Book It => new Book(){ 
    Title = "It", 
    Read = (pageNum) => ... // Do actual reading in delegate 
} 

EDIT: С более подробно о требованиях (но еще не все очевидно) я хотел бы сделать что-то вроде этого:

public class Tenant 
{ 
    // core things go here 
    public Extensions Extensions { get; } 
} 

public class Extensions : IEnumerable<IExtension> 
{ 
    private IList<IExtension> list = new List<IExtension(); 
    private Tenant { get; set; } 

    public Extensions(Tenant tenant) 
    { 
    Tenant = tenant; 
    } 

    public void Add(IExtension extension) 
    { 
    extension.Tenant = Tenant; 
    list.Add(extension); 
    } 
} 

public interface IExtension 
{ 
    Tenant { get; set; } 
    // shared interface of extensions if any can be abstracted 
} 

public interface ICoverPhotoExtension : IExtension 
{ 
    Photo GetCoverPhoto(); 
} 

public class FileCoverPhotoExtension : ICoverPhotoExtension 
{ 
    public Tenant { get; set; } 
    Photo GetCoverPhoto() { } // gets photo from file 
} 

public class BlogCoverPhotoExtension : ICoverPhotoExtension 
{ 
    public Tenant { get; set; } 
    Photo GetCoverPhoto() { } // gets photo from blog 
} 

использование:

Tenant tenant; // initialized somehow 
var coverPhotoExtension = tenant.Extensions.FirstOrDefault<ICoverPhotoExtension>(); 
Photo photo = coverPhotoExtension?.GetCoverPhoto(); 
+0

Спасибо за это - хотя это кажется немного «грязным» - что, если у меня есть 20 различных функций, которые нужно объявить? – SB2055

+0

Это определенно не предназначенное использование. –

+0

Это действительно круто и, вероятно, направление, в которое я пойду - спасибо! – SB2055

-1

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

public abstract class Book 
{ 
    public string Title {get; set;} 
    public abstract string Read(int pageNum); 
} 

public class ITBook : Book 
{ 
    public override string Read(int pageNum) 
    { 
     // Code here 
    } 
} 

Затем использовать класс вроде этого:

public static Book It => new ITBook() 
{ 
    Title = "It", 
} 

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

+0

«Вы можете использовать свойство класса делегата в классе Book для использования разных функций в разных экземплярах класса Book, но эти функции не будут иметь доступа к другим свойствам и методам экземпляра, к которому они будут использоваться». Это неправда. Если делегат был закрытием, но его нужно было бы построить после создания объекта книги. –

+0

Я стараюсь, чтобы размер кода был как можно меньше ... если у меня есть 10 разных книг - это 10 различных интерфейсов и статических классов = n * 2 вещей, о которых нужно беспокоиться. Есть ли простой метод, который позволяет мне достичь этого с n вещами, о которых нужно беспокоиться? – SB2055

+0

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

0
public Interface IBook{ 
    string Title {get; set;} 
    func<int,string> ReadPage 
} 

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

public class MyBook : IBook{ 

    public Title : {get;set;} = "MyBook"; 
    public func<int,string> ReadPage =(pagenumber)=>{ 
     return GetText(pagenumber); 
    } 
    public string GetText(int pageNumber){ 
     //read the page text by number here. 
    } 
} 

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

public static class XBook{ 
    public static string GetText(this IBook book, int pageNumber){ 
     ///do the work here and returng string 
    } 
} 

Чтобы использовать концепцию метода расширения:

using XBook; 
public class MyBook : IBook{ 

    public Title : {get;set;} = "MyBook"; 
    public func<int,string> ReadPage =(pagenumber)=>{ 
     return this.GetText(pagenumber); 
    } 
} 

Есть несколько способов сделать это ... сейчас, дайте ему попробовать.

+0

Я прокомментировал ответ NineBerry своими соображениями - это может быть лучше, но вы можете продемонстрировать его использование? – SB2055

+0

Обновлено сообщение для вас –