howto decouple in .NET?

  • Thread starter Thread starter Ben Voigt [C++ MVP]
  • Start date Start date
B

Ben Voigt [C++ MVP]

This is more of a C# question than a C++ question, but my best chance of
explaining it is via comparison to C++.

Ok:

In C++ you can forward declare a type. Then references to that type can be
passed around in a typesafe way without introducing a dependency on the
type. Only users needing to access the members need the type definition.

C# generics provide a similar capability, where you can treat a type as
opaque. But C# and .NET assume that the type argument will take on
different values, which isn't what I desire. One effect of this is that
while the C++ forward declaration is available to all code and automatically
refers to the same type everywhere, to get the same effect in C#, the type
arguments would spread like cancer. Also, since C# using directives are at
namespace scope, you can't use them to emulate a typedef, passing the type
arguments back and forth implicitly. What I'm looking for is a module-wide
set of type arguments.

Oh... I just had an idea... Let me see if C# supports generic static
classes. That might alleviate the pervasive spread of undefined types.
Actually, I might not want to declare the class itself static, because that
prevents inheritance. The C++ programmer in me cringes at the thought of
using a C# partial class as a more powerful namespace, but it might just
work.
 
This is more of a C# question than a C++ question, but my best chance of
explaining it is via comparison to C++.

Ok:

In C++ you can forward declare a type. Then references to that type can
be passed around in a typesafe way without introducing a dependency on the
type. Only users needing to access the members need the type definition.

C# generics provide a similar capability, where you can treat a type as
opaque. But C# and .NET assume that the type argument will take on
different values, which isn't what I desire. One effect of this is that
while the C++ forward declaration is available to all code and
automatically refers to the same type everywhere, to get the same effect
in C#, the type arguments would spread like cancer. Also, since C# using
directives are at namespace scope, you can't use them to emulate a
typedef, passing the type arguments back and forth implicitly. What I'm
looking for is a module-wide set of type arguments.

Oh... I just had an idea... Let me see if C# supports generic static
classes. That might alleviate the pervasive spread of undefined types.
Actually, I might not want to declare the class itself static, because
that prevents inheritance. The C++ programmer in me cringes at the
thought of using a C# partial class as a more powerful namespace, but it
might just work.

If I understand your question correctly, I think the usual way I have seen
this solved is through interfaces.
Several of my customers who did large projects in C# had guidelines that
specified that a class is always an implementation of x interfaces with very
little to no 'own' methods or properties.

That way they passed interfaces (A bit COM like if you want) to different
places.
This obviated the need for forward declarations, with the added contraint
that if you needed to cast from one thing to another, you had runtime type
checking instead of compile time type checking

Not sure if this helps you at all, but maybe it is useful.
 
Hi Ben,

Yes, I agree that posting in C# newsgroup will receive more C# MVP and
experts replies/thoughts. I am not sure I understand you completely.

Why don't you try System.Type as a member in a static class?

Static class A
{
Public TypeArg1 As System.Type
}

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
If I understand your question correctly, I think the usual way I have seen
this solved is through interfaces.
Several of my customers who did large projects in C# had guidelines that
specified that a class is always an implementation of x interfaces with
very little to no 'own' methods or properties.

That way they passed interfaces (A bit COM like if you want) to different
places.
This obviated the need for forward declarations, with the added contraint
that if you needed to cast from one thing to another, you had runtime type
checking instead of compile time type checking

I think I see what you're getting at here. I don't much like the part about
casting.

I have a system of widgets of various. Each widgets can, for simplicity's
sake, generate alarms and expose operations. I want to separate the code
dealing with these two concerns. That is to say, the alarm logger should be
able to collect alarms from all widgets without having to reference the code
declaring what you can do with an operation. The button for adjusting the
wazzit count should be able to find and run the right operation, without
caring about alarms. And the panic system is based on both, needing to
watch for critical alarms and invoke the reset operation on the widget that
caused the alarm.

With interfaces, I could make the component implement IAlarmSite and
IWithOperations. The alarm type could have a Producer property of type
IAlarmSite. The wazzit adjustment button could use methods from
IWithOperations. But... the panic system has a problem. It can find out
what IAlarmSite caused the critical alarm, but there are no methods for
running a reset operation because those are part of IWithOperations.
Runtime cast.

In C++ there would be no problem. The alarm type could have only a forward
declaration of the component class and return a pointer to it from
GetProducer(). The component class could expose an OperationSet* where that
also is an incomplete type. The alarm logger includes the component
definition and the alarm definition and can get details on what happened and
where. The wazzit adjuster can include the component definition and the
operationset definition and leave all the alarm stuff as incomplete types.
And the panic system can include all three. The only issue is that if
you're careless with your makefile dependency system, you can end up with
ODR violations.

..NET, with generics, has the potential to resolve even that problem, giving
perfect type safety (C++ templates work just as well except for requiring
compiling everything together).

generic<typename Component>
where Component : IAlarmSite
public ref class Alarm
{
...
property Component^ Producer
{
Component^ get( void ) { ... }
}
};

public ref class ComponentImpl : IAlarmSite, IWithOperations
{
typedef Alarm<ComponentImpl> Alarm;
...

BroadcastAlarm(gcnew Alarm());

...
};

and so forth.

But now those generic arguments spread uncontrollably, like a cancer.

public ref class AlarmCollection
{
List<Alarm^> ...; // uh-oh, Alarm needs a type argument
};

Take 2:

generic <typename Component>
where Component : IAlarmSite
public ref class AlarmCollection
{
typedef Alarm<Component> Alarm;
List<Alarm^> ...; // better
};

Take 3:

generic <typename Component>
where Component : IAlarmSite
public ref class AlarmConcern
{
public:
ref class Alarm
{
...
};

ref class AlarmCollection
{
...
};
};

public ref class Component : AlarmConcern<Component>, IAlarmSite
{
};

This is beautiful. The "forward declarations" are centralized and
automatically shared. The typedef is no longer needed, which is good
because it isn't available in C#. Configuration is also centralized, so
it's easy to substitute mock objects. There is just one fatal problem.
Inheritance is the only way to pull a generic class into the name search
scope, and .NET only allows single inheritance. C++/CLI can get around this
partway, with:

public ref class Component : IAlarmSite, IWithOperations
{
typedef AlarmConcern<Component> AlarmConfiguration;
using AlarmConfiguration::Alarm;
using AlarmConfiguration::AlarmCollection;
typedef OperationsConcern<Component> OperationsConfiguration;
// ....
};

This is not so nice anymore, because it requires a using declaration for
every class needed. And it isn't as useful in any language except C++/CLI.
The C# version of "using" can't be used inside a class, so it can't refer to
a generic parameter. Actually, I'm not sure if the C++/CLI should use
"using" or typedef, because the names aren't coming from a base class.

What I've seen a lot of requests for, and wanted a few times myself, is a
sort of "using members" directive, like using, except it pulls static
members (including nested types) of a class into the name search scope. But
this is where such a system would really shine... when used for
configuration of generics, with arbitrary nesting. Imagine:

public ref class Component : IAlarmSite, IWithOperations
{
using members AlarmConcern<Component>; // syntax 1
using OperationsConcern<Component>::*; // syntax 2

...
};

This totally obviates the perceived need to derive from a static (sealed
abstract) class. The power is because the generic configuration can be
passed though:

generic <typename Component>
where Component : IWithOperations
public ref class RemoteOperationsConcern
{
using OperationsConcern<Component>::*;

...
};

Can you suggest a means to accomplish something similar, with the existing
C++/CLI and C# languages?
 
"Jeffrey Tan[MSFT]" said:
Hi Ben,

Yes, I agree that posting in C# newsgroup will receive more C# MVP and
experts replies/thoughts. I am not sure I understand you completely.

I'm pretty sure how you understood me isn't even in the same universe as
what I meant.
Why don't you try System.Type as a member in a static class?

Static class A
{
Public TypeArg1 As System.Type
}

I said "In C++ you can forward declare a type. Then references to that type
can be passed around...". What I meant was "references to instances of that
type can be passed around". Please see my response to Bruno for
elaboration.
 
AlexS said:
Why you can't include ResetOperation method in IAlarmSite?

Because most users of alarms won't need it. Because if I include every
member needed by any user of alarms in IAlarmSite, I would have a huge
monolithic, tightly coupled, untestable, unmaintainable application. The
information I posted is a small subset of my total system. There's also
data being collected, trends being computed, an object lookup system,
conversions to various encodings, persistant configuration, and on and on.

Thanks for the advice, which has the advantages of being straightforward and
easy to implement, but it's not very effective on a large scale. The
stability vs abstraction metric of such a thing would be totally unworkable.
 
Ben Voigt said:
What I've seen a lot of requests for, and wanted a few times myself, is a
sort of "using members" directive, like using, except it pulls static
members (including nested types) of a class into the name search scope.
But this is where such a system would really shine... when used for
configuration of generics, with arbitrary nesting.

If I read your post correctly, and I may well be on a different planet, then
extension methods in C# 3.0 will do almost exactly what you're asking for.
http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx

When you use an extension method to "add" a method to a class, such as
"Squared" to the Int32 class, then that method shows up (to the outside
world) as part of the Int32 class. No extra casting or importing required.

Following your example, you would have you alarm operations extending (and
operating on) the alarm class.

Hrm. The more I think about it though, it's not really what you're asking
for.
 
If I got you right, you already have 2 interfaces on your widgets.

If inclusion of extra method in original interface is not acceptable, you
can create 3rd interface with required method, which will be used only in
"informed" clients.

As about "huge monolithic, tightly coupled, untestable, unmaintainable", I
am not sure I am in sync here. You have 2 interfaces already, so basically
your implementing class is that "huge... etc." entity. Also, I am not sure
that multiplying interfaces is making architecture small, non-monolithic,
testable etc...

Another questionable point is "most users won't need it". Generally speaking
it is impossible to predict what users will or won't need in the long run.

However, maybe I miss the real point here.

HTH
Alex
 
AlexS said:
If I got you right, you already have 2 interfaces on your widgets.

If inclusion of extra method in original interface is not acceptable, you
can create 3rd interface with required method, which will be used only in
"informed" clients.

As about "huge monolithic, tightly coupled, untestable, unmaintainable", I
am not sure I am in sync here. You have 2 interfaces already, so basically
your implementing class is that "huge... etc." entity. Also, I am not sure
that multiplying interfaces is making architecture small, non-monolithic,
testable etc...

You are assuming that there is a Reset method in some interface to provide
the reset operation. There is not. Each widget has a dictionary (lookup
table if you will) of the available operations, and each operation conforms
to a particular interface (think IDispatch::Invoke, it's not that far off).
The implementation of the operation is primarily not in the widget class,
there is a lot of machinery for discovering operations, logging calls,
remote invocation and so forth that is not part of the widget. It is
implemented separately and reused among a hierarchy of many different widget
classes derived from a common base. Moreover, none of this machinery needs
to be visible to the alarm logger, which doesn't execute any operations.
But it still has to be reachable from the alarm object by a caller who is
interested in calling an operation in response to an alarm, like the
hypothetical panic agent. There are a zillion combinations. Some callers
will execute operations depending on real-time data. Trends are updated
from real-time data. Operations may be triggered from trends. Alarms can
be triggered by real-time data, trends, or a combination. I'm trying to
avoid coupling all of the logic to an all-knowing widget class...

In native C++ I can maintain that encapsulation by using forward
declarations of classes. The widget can have a property exposing its
dictionary of available operations. Callers who aren't interested in
operations see an incomplete type. Callers who need to work on operations
include the associated header file.

In .NET, the way to say "there's this type defined elsewhere, meaningful to
some callers" is via generics. I'm trying to find a way to manage generics
in such a way that they don't avalanche.

Think how insane it would be for a program needing to show a window to be
dependent on digital certificates. Yet to show a window you need a window
handle. That window belongs to a process. That process runs in a logon
session. That session has an identity token. The identity token matches a
particular user. That user has keys to the encrypting file system. In .NET
1.x, none of those can be an incomplete type.
Another questionable point is "most users won't need it". Generally
speaking it is impossible to predict what users will or won't need in the
long run.

Sorry, I meant "callers (code using it)" rather than "users (people using
it)"
 
Hi Ben,

Sorry for the misunderstanding.

Perhaps you are looking for the mechanisms in C# for a ¡°compilation
firewall¡±, that is, a mechanism to decouple software components such that
both can compile independently and compilation propagation stops right
there. If that is the case, then the .NET interface type could help for
getting somehow similar effects, depending on the particular situation and
constraints, .NET reflection could be of help also.

In C++, one can inadvertently expose implementation details and increase
coupling: If Foo embeds a private member Bar, then every user of class Foo
also needs to know the layout of class Bar in order to use Foo. In managed
code, the problem doesn¡¯t exist ¨C if a class has a private member, the
rest of the world doesn¡¯t need to know of its existence. You can easily
verify this by declaring a public class Foo, which has a private reference
to an ¡°internal class Bar¡± declared in the same assembly.

However, using types ¡°in name only¡± is not possible in .NET. In C++, it
is possible for a class Foo to use Bar in its interface as an opaque handle
(if the public surface only refers to Bar* or Bar& and an instance of Bar
is NOT embedded in Foo, then the clients of Foo never need the definition
of Bar). Furthermore, if Foo never uses Bar directly in its implementation
(and only passes pointers or references to it to other components instead),
then Foo itself never needs the definition of Bar, either. This is not
achievable in C# - for instance, you cannot declare a public class Foo with
a public method taking or returning a Bar, if Bar is an internal class ¨C
you¡¯ll end up getting compiler errors.

If you are really committed to the lost art of uncoupling code, you might
want to look at ¡°the other way¡± of doing it ¨C declare Bar as internal
and don¡¯t use it in the public interface, but return and take IDs instead,
which you can then map to actual objects (in .NET, they can simply declare
their interface to use object and do some internal type checks prior to
using the thing). Note that there is a cost to this ¨C you will pay by
giving up some simplicity.

I don¡¯t think generics have anything to do with this. In .NET all types
must be fully defined at compilation time, generics is a run-time
mechanism, too late for a ¡°compilation firewall¡±.

Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
If I understand your question correctly, I think the usual way I have
I think I see what you're getting at here. I don't much like the part
about casting.

Yes, the interfaces indeed allow you to decouple parts of code. The only
downside -as you mention- is casting.

Personally, I have not yet encountered problems with that because the
implicit assumption is that the objects in question do support the required
interfaces, and that all functionality is implemented as an interface.
My experience is only with large in-house systems where the environment is
contained. i.e. the design has insured that when the project is code
complete, all expected interfaces are there.
 
However, using types ¡°in name only¡± is not possible in .NET. In C++, it
is possible for a class Foo to use Bar in its interface as an opaque
handle
(if the public surface only refers to Bar* or Bar& and an instance of Bar
is NOT embedded in Foo, then the clients of Foo never need the definition
of Bar). Furthermore, if Foo never uses Bar directly in its implementation
(and only passes pointers or references to it to other components
instead),
then Foo itself never needs the definition of Bar, either. This is not
achievable in C# - for instance, you cannot declare a public class Foo
with
a public method taking or returning a Bar, if Bar is an internal class ¨C
you¡¯ll end up getting compiler errors.
[snip]

I don¡¯t think generics have anything to do with this. In .NET all types
must be fully defined at compilation time, generics is a run-time
mechanism, too late for a ¡°compilation firewall¡±.

I'm not so sure about that. Generics allow exactly for a class to use
another type in its public interface without having a complete definition of
that type provided until runtime. Callers can also not have knowledge of
the type, if they are also generic.

Oh, but the constraints need to propagate to all callers... meaning the
interfaces need to be available again. But I think this time they can
propogate in from one side only... I'll see as I start trying to use this
method. The problem lies in avoiding clunky generic syntax all over the
place (with the corresponding need to make change everywhere when any new
generic type argument is needed.

I'm searching for a concept similar to what vhdl provides with
configurations.
 
Hi Ben,

So you may compare between these .Net/C# mechanisms and decide which one
meet your need, thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Back
Top