如何定义必须由类实例实现的函数的签名?

时间:2017-01-21 22:55:44

标签: c# c#-6.0

说我有一个班级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件事情。
  • 我需要添加大量这些自定义函数 - 通过构造函数传递Func似乎很笨重。
  • 实际上 - 这用于定义TenantTenants在内存中定义,并且有许多静态属性,如domainnameadminEmail等。这些是特定于租户的属性...但现在我我正在尝试实施特定于租户的行为 - 例如GetBooksFilterUsers。我希望尽可能简单地实现实现。现在我有"如果是TenantA,那么这样做,否则如果是TenantB,那么这样做......"洒在我的代码中。我试图在一个地方整合所有特定于租户的逻辑和细节 - 在Tenant类的实例上。
  • 租户特定行为的更多示例 - 您拥有SaaS论坛软件。在论坛A的主页上,您可以通过读取静态文件GetCoverPhoto。在论坛B的主页上,您可以通过博客主页阅读GetCoverPhoto。目前,我说"如果论坛A,这样做,否则如果论坛B,做这个"。这是我想要在Tenant对象上而不是在代码中定义的特定于租户的行为类型。我不想在我的核心逻辑中使用任何特定于租户的代码。

C#语言中是否有一个简单的功能/模式可以实现这一目标?

3 个答案:

答案 0 :(得分:5)

NineBerry所说的非常有效。

还有另外一种方法可以完成你想要的东西。如果要将read方法实现动态地注入Book中。这可以看作是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
}

编辑:有关要求的更多细节(但仍然不是一切都很明显)我会做这样的事情:

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

答案 1 :(得分:0)

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

使用包含继承。在上面的接口示例中,每本书中都有一个函数实现IBook,它将返回该页面的字符串。

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

我会使GetText成为类似于此的扩展方法,因此每本书都不需要实现GetText本身。

using XBook;
public class MyBook : IBook{

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

使用Extension方法概念:

{{1}}

有更多方法可以做到这一点......现在,试一试。

答案 2 :(得分:-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类的不同实例中使用不同的函数,但是这些函数无法访问它们将使用的实例的其他属性和方法。