简体   繁体   中英

How do I define the signature of a function that must be implemented by instances of a class?

Say I have a class Book :

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

I want every book to have a Read function that returns a string and accepts a page number - but the internals will be different for every book (poor example, I know). How do I define the signature of a function that must be implemented by instances of this class?

Something like this:

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?
}

My main concerns are:

  • Keeping things as simple as possible. Implementing interfaces under abstract classes works, but it gives me n*2 things to worry about as I add more instances.
  • I will need to add a large number of these custom functions - passing Funcs through constructors seems unwieldy.
  • In practice - this is used to define a Tenant . The Tenants are defined in-memory, and have many static properties like domain , name , adminEmail , etc. Those are tenant-specific properties... but now I am trying to implement tenant-specific behaviors - like GetBooks or FilterUsers . I would like to keep implementation as simple as humanly possible. Right now I have "If TenantA, do this, else if tenantB, do this..." sprinkled throughout my code. I'm trying to consolidate all tenant-specific logic and detail in one place - on instances of the Tenant class.
  • Further examples of Tenant specific behavior - you have a SaaS forum software. On the homepage of Forum A, you GetCoverPhoto by reading from a static file. On the homepage of Forum B, you GetCoverPhoto by reading from a blog homepage. Currently, I say "If Forum A, do this, else If Forum B, do this". This is the type of tenant-specific behavior that I want to define on the Tenant object, instead of in code. I don't want any tenant-specific code in my core logic.

Is there a simple feature/pattern in the C# language that will achieve this?

What NineBerry said is very valid.

There is one other way of accomplishing what you might want. If you want to dynamicly inject the read method implementation into a Book. This can be seen as strategy pattern . And can be done as interfaces like in many languages, but in simplest form in C# it can be done by delegates. Example:

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); }
}

Then use it as:

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

EDIT: With more detail on requirements (but still not everything is obvious) I would do something like this:

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
}

usage:

Tenant tenant; // initialized somehow
var coverPhotoExtension = tenant.Extensions.FirstOrDefault<ICoverPhotoExtension>();
Photo photo = coverPhotoExtension?.GetCoverPhoto();
public Interface IBook{ 
   string Title {get; set;}
   func<int,string> ReadPage
}

Use containment over inheritance. In the interface example above there is a function in every book that implements IBook that will return a string for that page.

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.
   }
}

I would make GetText an extension method similar to this, so each book doesn't need to implement GetText itself.

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

To use the Extension method concept:

using XBook;
public class MyBook : IBook{

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

There are more ways to do this... for now, give it a try.

Having different instances of a class implement functions differently is not possible. Instead, you inherit a new class where you implement the separate behaviour.

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
   }
}

Then use the class like this:

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

You could use a property of a delegate type in the Book class to use different functions in different instances of the Book class, but these functions would not have access to the other properties and methods of the instance they would be used by.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM