Reputation: 85
Say I have the following interfaces:
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> {...}
Initially, the errors force me to have implementations for the ReturnAs
, ImageResolution
and IsPretty
properties. Making these implementations protected
end up with CS0535
, saying that the properties weren't implemented. On the other hand, making these public
gave me CS8704
, telling that this implementation is not possible.
Other than abstract classes, would there be workarounds for these errors?
Upvotes: 1
Views: 2170
Reputation: 949
There is a solution here, though I am not sure if I like it.
IReturnableAsImage<T>
and IReturnableAsJson<T>
can extend IReturnableAs
and hide its ReturnAs
method with a new
ReturnAs
method.
They can, and probably should, also explicitly override IReturnableAs
's ReturnAs
property to save a concreate implementation from having to do so; see the implementation of Bar
below.
Since Foo
implements both IReturnableAsImage<T>
and IReturnableAsJson<T>
though, it will have to provide an implementation for IReturnableAs.ReturnAs
as well.
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
doesn't need to implement IReturnableAs.ReturnAs
as IReturnableAsImage<Bar>
already does this. Though it may to "overload" (reimplement may be a better term) ReturnAs
implicitly or either IReturnableAs.ReturnAs
or IReturnableAsImage<Bar>.ReturnAs
explicitly, in any combination.
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
on the other hand must explicitly reimplement IReturnableAs.ReturnAs
implicitly or ReturnAs
explicitly (or both) since both IReturnableAsImage and IReturnableAsJson provide implementations for this property.
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;
}
The results work as expected, though changing what the concrete methods reimplement will change the results, possibly unexpectedly.
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)");
}
Output:
Bar:
image - (IReturnableAs)
image - (IReturnableAsImage<Bar>)
Foo:
image;json - (IReturnableAs)
image - (IReturnableAsImage<Foo>)
json - (IReturnableAsJson<Foo>)
You may want to comment in the commented out lines of Foo
and Bar
above to see what gets overloaded and when.
For example, if Bar
explicitly implements ReturnAs
it overrides default implementations of both IReturnableAs
and IReturnableAsImage<Bar>
unless Bar
implicitly implements them as well.
Upvotes: 0
Reputation: 131324
First of all, what do you want to achieve? What should Foo.ReturnAs
return? How are you going to use those interfaces?
You can't use ReturnAs
in the other interfaces without inheriting from IReturnableAs
. Once you inherit from that interface though Foo
will have to provide an implementation. When that happens, no matter how you cast Foo
you'll always get its own IReturnableAs
implementation.
Interfaces aren't abstract classes so there can only be one interface member implementation. You can't access different "default" implementations through different interfaces.
Composite result when accessed through IReturnableAs
If you want to return json
or image
for the specific interfaces and image;json
for Foo
overall the best option would be for the interfaces to not inherit from IReturnableAs
, and provide their own ReturnAs
property :
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;
}
The following code :
void Main()
{
var foo=new Foo();
Console.WriteLine(((IReturnableAs)foo).ReturnAs);
Console.WriteLine(((IReturnableAsImage<Foo>)foo).ReturnAs);
Console.WriteLine(((IReturnableAsJson<Foo>)foo).ReturnAs);
}
Prints:
image;json
image
json
I removed the ReturnAs
setters since the valid value will always be the same for the same interface.
If you want to create a new class that generates JPGs, eg FooJpg
, you can override the default implementation of IReturnableAsImage<T>
, eg :
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";
}
Same result no matter the interface
If you want Foo.ReturnAs
to always return the same value, eg "image;json"
, you can add a default IReturnAs
implementation for single use cases, and override the method for multiple uses :
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;
}
}
In this case the IReturnableAsImage
, IReturnableAsJson
interfaces provide an implementation. For this class :
public class Foo : IReturnableAsImage<Foo>
{
String IReturnableAsImage<Foo>.ImageResolution { get; set; }="3";
}
The following code will print image
:
void Main()
{
var foo=new Foo();
Console.WriteLine(((IReturnableAs)foo).ReturnAs);
Console.WriteLine(((IReturnableAsImage<Foo>)foo).ReturnAs);
}
For a class that uses both interfaces, an explicit IReturnableAs
implementation is needed:
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;
}
In this case all calls will 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
Upvotes: 1