C#8.0:具有默认实现的接口的受保护属性

C#8.0: Protected properties for interfaces with default implementations

假设我有以下接口:

public interface IReturnableAs {
  protected String ReturnAs { get; set; }
}
public interface IReturnableAsImage<T> {
  protected String ImageResolution { get; set; }
  public T ReturnAsImage(String imageResolution = "large") {
    ReturnAs = "image";
    ImageResolution = imageResolution;
    return (T)this;
  }
}
public interface IReturnableAsJson<T> {
  protected Boolean IsPretty { get; set; }
  public T ReturnAsJson(Boolean isPretty = false) {
    ReturnAs = "json";
    IsPretty = isPretty;
    return (T)this;
  }
}

public class Foo : IReturnableAsImage<Foo>, IReturnableAsJson<Foo> {...}

最初,这些错误迫使我实现 ReturnAsImageResolutionIsPretty 属性。使这些实现 protectedCS0535 结束,表示属性未实现。另一方面,制作这些 public 给了我 CS8704,告诉我这个实现是不可能的。

除了抽象 类,这些错误是否有解决方法?

首先,你想达到什么目的?什么应该Foo.ReturnAsreturn?您将如何使用这些界面?

如果不继承IReturnableAs,则不能在其他接口中使用ReturnAs。一旦您从该接口继承,尽管 Foo 将必须提供一个实现。发生这种情况时,无论您如何转换 Foo,您总是会得到它自己的 IReturnableAs 实现。

接口不是抽象的 class 因此只能有 一个 接口成员实现。您不能通过不同的接口访问不同的 "default" 实现。

通过 IReturnableAs 访问时的复合结果

如果您想要 return jsonimage 用于特定接口,image;json 用于 Foo 总体而言,最好的选择是接口继承自IReturnableAs,并提供自己的ReturnAs 属性 :

public interface IReturnableAs {
  public String ReturnAs { get; }
}
public interface IReturnableAsImage<T>
{
  public String ReturnAs =>"image";

  protected String ImageResolution { get; set; }    
  public T ReturnAsImage(String imageResolution = "large") 
  {
    ImageResolution = imageResolution;
    return (T)this;
  }
}
public interface IReturnableAsJson<T> {
  public String ReturnAs =>"json";

  protected Boolean IsPretty { get; set; }
  public T ReturnAsJson(Boolean isPretty = false) {
    IsPretty = isPretty;
    return (T)this;
  }
}

public class Foo : IReturnableAsImage<Foo>, IReturnableAsJson<Foo> ,IReturnableAs
{
    string IReturnableAs.ReturnAs =>"image;json";

    String IReturnableAsImage<Foo>.ImageResolution { get; set; }="3";
    Boolean IReturnableAsJson<Foo>.IsPretty { get; set; }=false;
}

以下代码:

void Main()
{
  var foo=new Foo();
  Console.WriteLine(((IReturnableAs)foo).ReturnAs);
  Console.WriteLine(((IReturnableAsImage<Foo>)foo).ReturnAs);
  Console.WriteLine(((IReturnableAsJson<Foo>)foo).ReturnAs); 
}

打印:

image;json
image
json

我删除了 ReturnAs 设置器,因为同一接口的有效值将始终相同。

如果您想创建一个新的 class 生成 JPG,例如 FooJpg,您可以覆盖 IReturnableAsImage<T> 的默认实现,例如:

public class FooJpg : IReturnableAsImage<FooJpg>, IReturnableAsJson<FooJpg> ,IReturnableAs
{
    string IReturnableAs.ReturnAs =>"jpg;json";

    String IReturnableAsImage<FooJpg>.ImageResolution { get; set; }="3";
    Boolean IReturnableAsJson<FooJpg>.IsPretty { get; set; }=false;

    String IReturnableAsImage<FooJpg>.ReturnAs => "jpg";

}

无论界面如何,结果都一样

如果您希望 Foo.ReturnAs 始终 return 相同的值,例如 "image;json",您可以为单个用例添加默认的 IReturnAs 实现,并覆盖多种用途的方法:

public interface IReturnableAs {
  public String ReturnAs { get; }
}

public interface IReturnableAsImage<T>:IReturnableAs
{
  String IReturnableAs.ReturnAs =>"image";

  protected String ImageResolution { get; set; }    
  public T ReturnAsImage(String imageResolution = "large") 
  {    
    ImageResolution = imageResolution;
    return (T)this;
  }
}

public interface IReturnableAsJson<T>:IReturnableAs { 
  String IReturnableAs.ReturnAs =>"json";

  protected Boolean IsPretty { get; set; }
  public T ReturnAsJson(Boolean isPretty = false) {
    //ReturnAs="json";
    IsPretty = isPretty;
    return (T)this;
  }
}

在这种情况下,IReturnableAsImageIReturnableAsJson 接口提供了一个实现。为此 class :

public class Foo : IReturnableAsImage<Foo>
{
    String IReturnableAsImage<Foo>.ImageResolution { get; set; }="3";
}

以下代码将打印 image:

void Main()
{
  var foo=new Foo();
  Console.WriteLine(((IReturnableAs)foo).ReturnAs);
  Console.WriteLine(((IReturnableAsImage<Foo>)foo).ReturnAs);
}

对于使用两个接口的 class,需要显式 IReturnableAs 实现:

public class FooMulti : IReturnableAsImage<FooMulti>, IReturnableAsJson<FooMulti> 
{
    String IReturnableAs.ReturnAs =>"image;json";

    String IReturnableAsImage<FooMulti>.ImageResolution { get; set; }="3";
    Boolean IReturnableAsJson<FooMulti>.IsPretty { get; set; }=false;
}

在这种情况下,所有调用都将 return image;json :

void Main()
{
  var foo=new FooMulti();
  Console.WriteLine(((IReturnableAs)foo).ReturnAs);
  Console.WriteLine(((IReturnableAsImage<FooMulti>)foo).ReturnAs);
  Console.WriteLine(((IReturnableAsJson<FooMulti>)foo).ReturnAs); 
}

image;json
image;json
image;json

这里有一个解决方案,虽然我不确定我是否喜欢它。

IReturnableAsImage<T>IReturnableAsJson<T> 可以扩展 IReturnableAs 并使用 new ReturnAs 方法隐藏其 ReturnAs 方法。

他们可以,而且可能应该,也明确地覆盖 IReturnableAsReturnAs 属性 来避免必须这样做的具体实现;请参阅下面 Bar 的实施。

尽管 Foo 同时实现了 IReturnableAsImage<T>IReturnableAsJson<T>,它也必须为 IReturnableAs.ReturnAs 提供实现。

public interface IReturnableAs
{
    public String ReturnAs { get; }
}

public interface IReturnableAsImage<T> : IReturnableAs
{
    // implicitly "override" IReturnableAs's ReturnAs
    string IReturnableAs.ReturnAs => ReturnAs; 
    
    // use "new" to indicate hiding on purpose
    public new string ReturnAs => "image"; 

    protected string ImageResolution { get; set; }

    public T ReturnAsImage(String imageResolution = "large")
    {
        ImageResolution = imageResolution;
        return (T)this;
    }
}

public interface IReturnableAsJson<T> : IReturnableAs
{
    // implicitly "override" IReturnableAs's ReturnAs
    string IReturnableAs.ReturnAs => ReturnAs;

    // use "new" to indicate hiding on purpose
    public new string ReturnAs => "json";

    protected bool IsPretty { get; set; }

    public T ReturnAsJson(Boolean isPretty = false)
    {
        isPretty = isPretty;
        return (T)this;
    }
}

Bar 不需要实施 IReturnableAs.ReturnAs 因为 IReturnableAsImage<Bar> 已经这样做了。虽然它可能会“过载”(重新实现可能是一个更好的术语)隐式地 ReturnAs 或显式地 IReturnableAs.ReturnAsIReturnableAsImage<Bar>.ReturnAs,以任何组合。

public class Bar : IReturnableAsImage<Bar>
{
 // public string ReturnAs => "implicit ReturnAs";
 // string IReturnableAs.ReturnAs => "explicit IReturnableAs.ReturnAs";
 // string IReturnableAsImage<Bar>.ReturnAs => "explicit IReturnableAsImage<Bar>.ReturnAs";

    string IReturnableAsImage<Bar>.ImageResolution { get; set; } = "3";
}
另一方面,

Foo 必须明确地重新实现 IReturnableAs.ReturnAs 隐式或 ReturnAs 显式(或两者),因为 IReturnableAsImage 和 IReturnableAsJson 都为此 属性 提供了实现。 =44=]

public class Foo : IReturnableAsImage<Foo>, IReturnableAsJson<Foo>
{
 // public string ReturnAs => "implicit";
 // string IReturnableAsImage<Bar>.ReturnAs => "explicit image";
 // string IReturnableAsJson<Bar>.ReturnAs => "explicit json";

    string IReturnableAs.ReturnAs => "image;json";

    string IReturnableAsImage<Foo>.ImageResolution { get; set; } = "3";
    bool IReturnableAsJson<Foo>.IsPretty { get; set; } = false;
}

结果按预期工作,但改变重新实现的具体方法会改变结果,这可能出乎意料。

void Main()
{
    var bar = new Bar();
    Console.WriteLine("Bar: ");
    Console.WriteLine(((IReturnableAs)bar).ReturnAs + " -  (IReturnableAs)" );
    Console.WriteLine(((IReturnableAsImage<Bar>)bar).ReturnAs + " - 
 (IReturnableAsImage<Bar>)");

    // only works when ReturnAs is explicitly implemented on Bar
 // Console.WriteLine(bar.ReturnAs + " -  (Bar)"); 

    Console.WriteLine();

    var foo = new Foo();
    Console.WriteLine("Foo:");
    Console.WriteLine(((IReturnableAs)foo).ReturnAs + " -  (IReturnableAs)");
    Console.WriteLine(((IReturnableAsImage<Foo>)foo).ReturnAs + " -  (IReturnableAsImage<Foo>)");
    Console.WriteLine(((IReturnableAsJson<Foo>)foo).ReturnAs + " -  (IReturnableAsJson<Foo>)");

    // only works when ReturnAs is explicitly implemented on Foo 
 // Console.WriteLine(foo.ReturnAs + " -  (Foo)");  
     }

输出:

Bar:
image -  (IReturnableAs)
image -  (IReturnableAsImage<Bar>)

Foo:
image;json -  (IReturnableAs)
image -  (IReturnableAsImage<Foo>)
json -  (IReturnableAsJson<Foo>)

您可能想在上面 FooBar 的注释掉的行中发表评论,以查看过载的内容和时间。

例如,如果 Bar 显式实现 ReturnAs,它将覆盖 IReturnableAsIReturnableAsImage<Bar> 的默认实现,除非 Bar 也隐式实现它们。