Stack Overflow Asked by batekha on December 2, 2020
Say I have
public enum DataFlow{
Input,
Output
}
public interface IPort{
DataFlow Dir{get;}
}
public class Port<T>: IPort
{
DataFlow Dir{get;}
T Val;
public Port(T val, DataFlow dir){
Val = val;
Dir = dir;
}
}
public class Link{
public Link(IPort portA, IPort portB){
}
}
In the Link
class above, is there a way to constrain the constructor so that it only accepts portA
if its Dir
property is DataFlow.Output
, and portB
if its Dir
property is DataFlow.Input
?
Something like this imaginary made-up syntax…
public Link(IPort portA where IPort.Dir is DataFlow.Output, IPort portB where IPort.Dir is DataFlow.Input){
}
I can do that by checking Dir
of each inside at the constructor then throwing if needed, but I was wondering if there was a way to enforce that much like the [NotNull]
and where T :
c# syntax
You can't constrain the formal parameters like that. What you can do, is to make the Dir
property a part of the type system, and let the type checker check it.
Basically, this means that input ports and output ports should be different types. One way to do this is:
public interface IPort {
// anything else other than Dir, that you want to put in IPort
}
public interface IInputPort : IPort {}
public interface IOutputPort : IPort {}
public class InputPort<T>: IInputPort
{
T Val;
public InputPort(T val){
Val = val;
}
}
public class OutputPort<T>: IOutputPort
{
T Val;
public OutputPort(T val){
Val = val;
}
}
The constructor could then be declared as:
public Link(IOutputPort portA, IInputPort portB){
}
Rather than passing in the DataFlow
you want as the parameter of the Port
constructor, you should just instantiate the desired port class - OutputPort
or InputPort
.
You can still have your Dir
property, but as an extension method:
public static class PortExtensions {
public static DataFlow Dir(this IPort port) {
if (port is IInputPort) return DataFlow.Input;
if (port is IOutputPort) return DataFlow.Output;
throw new ArgumentException("port must be either input port or output port!", nameof(port));
}
}
Note that this creates a lot of code duplication if InputPort
and OutputPort
shares a lot of code, but this can be easily solved by creating an abstract AbstractPort
base class that both InputPort
and OutputPort
inherit from, and putting the common code there.
Correct answer by Sweeper on December 2, 2020
No, because this doesn't make sense. "where" is a design time construct that has the compiler check the type in a generics scenario and you're trying to apply it to a runtime data setting
I suggest you consider using the typing mechanism instead and have an IInputPort and an IOutputPort, and then your code can later check what the passed in implementation is
(if for some reason you don't store it as an IInputPort/IOutportPort) to know whether it's input or output (because that's really what you're asking- you want to make sure that something passed to your class is capable of acting as an eg an input). The implementer of your code is free to design one class that implements both and pass an instance of it to each, of course
Alternatively, you check the value passed in and throw a meaningful exception if it's not acceptable. This will hopefully be picked up on as part of the design time process of using your class, by the developer doing the implementation before their class is put into part of a production system.. but it does push the risk that errors in implementation won't be picked up until later down the software development line (possibly after their impl of your code has gone live)
Answered by Caius Jard on December 2, 2020
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP