wrote:
The difference is that A has ConsumeB, which takes an IB as aThat's not a difference. The code example you compared to has the
parameter.
implication that A will use the IB instance passed back from the GetB
method. After all, that's why the return type for GetB is IB, rather than
B.
It can't take a B as a parameter, because B is private toThat's why you define the interface in a way that provides the guarantee
A. So it only has strong guarantees about the methods that are
available, not what they do.
you need. This doesn't enforce the semantics, but it does provide
implementors with very strond indications regarding what the
implementations should do
For example:
Well, in the rather more complicated example that inspired this, BIn that case, you need to write an interface that represents itself only
contains a transformation matrix which, at least when constructed by
A, is known to be a rotation. It's quite awkward (though certainly
possible) to verify if a given matrix is a pure rotation, but it's
quite easy to compose one out of other known rotations.
in terms of rotation. It can return a matrix (cloned, so that you don't
allow external mutation of internal state) as a convenience, but the API
should be strictly in terms of rotating. Even better is for the interface
to be immutable. You can only create an instance by providing a rotation,
or by combining existing instances. The interface would include the
latter operation, and of course the implementor provides the former.
You could either make the assumption that the implementor has followed the
semantics of the interface or, if you really think it's that important,
it's not hard to inspect a transformation matrix and determine whether it
only rotates. This is more flexible than insisting on your own
implementation, if doing that bothers you.
Personally, I think it's fine to make an assumption about whether the
interface is implemented with the intended semantics. For one, you never
know: perhaps some day someone will find a need to provide an
implementation that does something other than just rotate. For another,
if it's true that your own code will really break unless that condition is
met, then that should be readily apparent as soon as someone tries to use
an incorrect implementation. To some extent, I find it reasonable to rely
on testing to ensure correctness. Not everything about the correctness of
the code can be represented by the API that code exposes.
That said, I have the impression that you're not all that enthusiastic
about using interfaces to address this. And that's fine.
[...]Well, as has been noted, you can. I think that using "internal" is the>You can't really guarantee that. But you can, as I mentioned in my
previous reply to your message, at least check the type of the
implementing instance to make sure it's B. Presumably that's really all
you care about anyway.
I certainly *could* do, but that feels very against-the-grain. It
seems like it should be possible to solve this at compile-time. I was
inspired by recent articles by Eric Lippert, such as:
http://blogs.msdn.com/ericlippert/ar...why-can-i.aspx
as well as his previous series on immutable data types. It made me
think that I could and should be using the type system and access
controls to provide stronger guarantees about the behaviour of my
classes.
best way, personally. I agree that nesting the classes can get out of
hand, and for no real good reason.
You might need to adjust your thinking, so that you're willing to treat
assemblies as legitimate compilation divisions. But it seems like they
offer the separation you're looking for here.
Pete