This is valid C#:
interface IAnInterface
{
IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
interface IPropertyThatIsAnInterface
{
int X { get; set; }
}
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
You could do this
class ClassThatImplementsIAnInterface : IAnInterface
{
public ClassThatImplementsIPropertyThatIsAnInterface InterfaceImplmentationProperty { get; set; }
public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
class ClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
public int X { get; set; }
}
Please note that InterfaceImplmentationProperty
has nothing to do with IAnInterface
.
We can make implement InterfaceProperty
explicitly, which hides it:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty { get; set; }
Yet, InterfaceProperty
and InterfaceImplmentationProperty
are still separate. Let us delegate InterfaceProperty
to InterfaceImplmentationProperty
:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
get => InterfaceImplmentationProperty;
set => InterfaceImplmentationProperty = value; // ERROR
}
Now, we have an error. For, you see, an IPropertyThatIsAnInterface
is not necesarily an InterfaceImplmentationProperty
. However, IAnInterface
promises I can set any IPropertyThatIsAnInterface
there.
If we continue this path, we got to subvert expectation, and throw an exception:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
get => InterfaceImplmentationProperty;
set => InterfaceImplmentationProperty = (ClassThatImplementsIPropertyThatIsAnInterface)value;
}
Here, I have added a cast, this might fail on runtime. It can be easy to ignore... We can be a bit more expressive:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
get => InterfaceImplmentationProperty;
set
{
if (!(value is ClassThatImplementsIPropertyThatIsAnInterface valueAsSpecificType))
{
throw new ArgumentException($"{nameof(value)} is not {typeof(ClassThatImplementsIPropertyThatIsAnInterface)}", nameof(value));
}
InterfaceImplmentationProperty = valueAsSpecificType;
}
}
Alright, we saw above that we would have to bend the contract of the interface to make it work. So... how about making the contract more flexible?
We start by making the interface generic:
interface IAnInterface<TPropertyThatIsAnInterface>
where TPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
TPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
Just following your naming style here.
Then we can specify the type when we implement it:
class ClassThatImplementsIAnInterface : IAnInterface<ClassThatImplementsIPropertyThatIsAnInterface>
{
public ClassThatImplementsIPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
If you really want the uglier name, you can do the explict implementation and delegated property thing.
To go further... what is the purpose of the interface? Isn't it so you can deal with the objects via the interface and not have to deal with specific types? - Ideally, you should not have to cast or check types.
From comment:
I have implemented your suggestion but am having an issue when trying to populate a List<IFileProcessors<IProcessorParameters>>
. I get a "cannot implicitly convert type" error.
See? You want to treat them as the same type. Then make them the same type.
Well, there is a pattern for that, which is to have two version of the interface, one generic and that isn't. Then the generic interface inherits from the non-generic. I'd say, if you can avoid that, avoid it. If anything, it will lead to more type checks in runtime.
Ideally you should be able to deal with the type via its interface. The interface should be enough. So that the specific type is not required, and thus neither a cast to use it.
As I was explaining above the setter is a problem.
If the actual type of the property is more specific than the one on the interface, then the interface says that the property allows types that the class does not.
Can you remove the setter?
interface IAnInterface
{
IPropertyThatIsAnInterface InterfaceProperty { get; }
}
interface IPropertyThatIsAnInterface
{
int X { get; set; }
}
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; }
}
ClassThatImplementsIAnInterface
will still be able to initialize InterfaceProperty
with any type that implements IPropertyThatIsAnInterface
. The consumer would not have to be aware of it. And, asuming IPropertyThatIsAnInterface
is useful as it is (it should) there should be no need to cast it to use it. In hinges on whatever or not the consumer can deal with the interface. At the moment when the consumer needs a particular type, you will be casting.
{ get;set; }
? I think I see where you're coming from because I tried to define a default getter in the interface but it didn't work as I expected. If auto-properties cannot be defined in this way, then I guess that's why it doesn't work? – Bolinint MyInt { get; set; }
andpublic int MyInt { get; set; }
are equivalent in an interface). What you've got is technically valid, but most C# devs would not include thepublic
modifier in an interface. – Nunnally