Circular Referencing in C#

P

PromisedOyster

Hi

I have a situation where I want to use circular referencing. I have cut
down the example for demonstration purposes.

Say we have one executable (main.exe) and two DLLS (A1.dll and A2.dll).
Main can access classes in A1 and A2; A1 can access classes in A2.

However, I now want to access classes in A1 from A2. I cannot simply
create a third DLL as the A1/A2 assemblies use other classes within
their respective assemblies.

I believe that I can use interface classes to solve this situation.
However, to be honest, I'm not sure how?

An example would be very useful....


eg in main.exe
--------------
using A1;
using A2;

A1C1 a1c1 = new A1C1();
A2C1 a2c1 = new A2C1();


A1.DLL
------
using A2;
namespace A1
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class A1C1
{
public A1C1()
{
A2C1 c = new A2C1();
}
}
public class A1C2
{
public A1C2()
{
// I want access thi class from A2
}
}
}

A2.DLL
------

namespace A2
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class A2C1
{
public A2C1()
{
// Can access this class from A1
}
}
public class A2C2
{
public A2C2()
{
// I now want to access A1C2??????
}
}
}
 
S

sadhu

Hi,

Declare an interface for A1C2 in a separate assembly. Refer it from
A2. And when you pass an instance of A1C2, pass it as an interface.

Regards
Senthil
 
P

PromisedOyster

Hi sadhu

Could you please provide me with an example of what you mean.

This would be very useful. I am not sure why there is a need for the
separate assembly?
 
B

Bruce Wood

It would help to know what you mean by "access" A1C2 from A2C2... do
you mean accept as a parameter, create a new instance... what do you
need to do with it?

Senthil's solution is the one that applies to all situations: create a
new assembly that contains interfaces. These interfaces are contracts
that the classes in the other two assemblies agree to honour. Then each
assembly deals with the other in terms of interfaces.

However, in limited situations, you may be able to get away with
putting the interfaces in A2, which would leave you with a one-way
references from A1 to A2, but never the other direction.

Could you give more details on what you need to do?
 
P

PromisedOyster

Bruce said:
It would help to know what you mean by "access" A1C2 from A2C2... do
you mean accept as a parameter, create a new instance... what do you
need to do with it?

Senthil's solution is the one that applies to all situations: create a
new assembly that contains interfaces. These interfaces are contracts
that the classes in the other two assemblies agree to honour. Then each
assembly deals with the other in terms of interfaces.

However, in limited situations, you may be able to get away with
putting the interfaces in A2, which would leave you with a one-way
references from A1 to A2, but never the other direction.

Could you give more details on what you need to do?

I want to create an instance of the class A1C2 from A2C2

ie
public A2C2()
{
A1C2 c = new A1C2();
}
 
O

Olorin

Have you tried writing just that ?
(ok, with a using A1 statement at the top).
A1C2 does not have a field of any type in A2, so instanciating an
object of type A1C2 does not create a circular reference.... unless I'm
missing something.

HTH,
F.O.R.
 
J

Joanna Carter \(TeamB\)

I want to create an instance of the class A1C2 from A2C2

From an OO design perspective, what you are trying to do seems overly
complicated and has a 'bad smell' about it.

If multiple classes have such strong interdependencies, then it is likely
that they exist within the same assembly at least.

This kind of design problem is a symptom of more problems to come, as you
code becomes more and more convoluted.

Can you share some real names for these classes, so that we can advise on
the design ?

Joanna
 
A

Ajay Kalra

If you have both classes sort of depending upon each other, why do you
have a separate assembly for each? You can make it work using
interfaces etc, but you should look into refactoring your dlls and
possibly bringing the functionality from A2 into A1 that you want in
A1.
 
B

Bruce Wood

I think I understand what the OP is trying to do. It's actually very
good design... it's just that it's a trick trying to figure out how to
do it in C#. If I'm reading this right, consider the following:

A classic three-tiered design. The UI component talks only to the
business layer, never to the data layer. The business layer
encapsulates business rules, but also knows how to talk to the data
layer to load and fetch instances of itself. The data layer would like
to be ignorant of the the other two layers, but unfortunately it can't
be, because it has to know how to build and return business objects
that it has fetched from the database.

In the end, one ends up with a circular reference between the business
layer and the data layer. The interdependence isn't really all that
strong... it's just that the data layer builds and returns business
objects (and accepts them in order to insert / update them in the
database), and the business layer knows how to call various top-level
data layer methods, usually CRUD interfaces to the database that
abstract data layer operations to business layer terms.

It's a good design, but it causes problems in C#, which doesn't allow
circular DLL references for reasons that have everything to do with how
the compiler / languages works and little (or maybe nothing) to do with
enforcing good design.

One way to solve this problem in C# is simply to abandon the division
into two DLLs. After all, your data layer and your business layer are
tightly coupled, so you're never going to use one without the other.
The division into two DLLs for two tiers may seem aesthetically
pleasing, but it really serves no practical purpose.

Another way to solve the problem is to have your business layer publish
a data layer interface, in effect saying, "These are the data layer
object signatures that I require in order to operate." Part of the
initialization of the system then becomes pointing the business layer
to the actual data layer that it is going to be using. The data layer
must then be sure to implement all of the interfaces in order that the
business layer can use it. Conceptually, what is happening here is that
your business layer is creating a demand contract: "I demand that the
data layer provide the following functionality. I will work with any
data layer that provides this functionality." So the static DLL
references become: UI layer references business layer; data layer
references business layer. The business layer references nothing but
interface definitions, and is connected to the true data layer at run
time.

You could also completely change the design and say that the data layer
returns data objects, not business objects, so calling data layer
functions returns DataSets, or DataTables, or DataRows. It then becomes
the responsibility of the business layer to put a business veneer on
this "pure data". This is more the approach that Microsoft has taken:
data layers return Data...whatever objects; Visual Studio will build
classes for you that front these DataTables, etc. It's then up to you
to build business logic around what is really just pure data. (By
subclasing the auto-generated classes? I haven't tried this yet.) In
this scenario, your UI layer has a static reference to your business
layer, and your business layer to your data layer. The data layer has
no reference to the business layer because it returns information on
its own terms, not conveniently packaged for the business layer.
Personally, I find this design a little uglier than the preceding one:
a tier should really cater to the next tier up in the hierarchy,
packaging its functions and results for the convenience of its "client"
tier. It's "bad software behaviour" to say to your calling tier,
"here's what you asked for in the form that is convenient for _me_ to
deliver it. You deal with any disconnects between that and your world
view." However, as I said, this is more how MS is doing things, so it's
worth considering.
 
J

Joanna Carter \(TeamB\)

A classic three-tiered design. The UI component talks only to the
business layer, never to the data layer. The business layer
encapsulates business rules, but also knows how to talk to the data
layer to load and fetch instances of itself. The data layer would like
to be ignorant of the the other two layers, but unfortunately it can't
be, because it has to know how to build and return business objects
that it has fetched from the database.

What you are talking about is something with which I have a great deal of
experience :)

I have done all of this in Delphi which has even tighter restrictions on not
having circular references between units (code modules). I have designed
three main frameworks that provide all the services necessary for completely
non-circular management of business, storage and UI layers.

The frameworks are :

Value Type (not the same as ValueType :) ) - this provides more metadata
than reflection and allows you to detect when an object goes dirtyas well as
providing validation callbacks.

Object Persistence - this is a mechanism that allows you to change
databases, even in mid-execution without changing any of your client code.
All objects are retrieved from or passed to the Persistence Broker, which
examines the ValueTypes and dynamically creates SQL, XML, CSV or whatever
you want to use for storage/retrieval/etc.

Model View Presenter - this is a very sophisticated version of MVC which
allows the management of UI interaction to be completely separated from the
business layer. It also uses the Value Types to obtain the data that it
requires to display.

There is absolutely no knowledge of the business layer in either the OPF or
MVP frameworks.

I have written several articles on these subjects; if you are interested,
they can be found on my website (I'm not very good at web design)
www.carterconsulting.org.uk. You might also find some of my writings on
Borland's Code Central.

I teach OO design more than I do anything else; and am now enjoying learning
all about Delphi Mk2 (C#) <vbg> I am also a member of Borland"s TeamB which
is their equivalent of Microsoft's MVPs.

Joanna
 
A

Ajay Kalra

I come from a strong C++ background (non-web). Circular references are
something you dont want to go near. C++ gives you some power to deal
with it but it inherently means bad design. A winform app(or using MFC
in unmanaged world) would have the same datastructure as you defined. I
see absolutely no reason for data to know who its clients are. IOW,
there is clear demarcation between Data and its clients. Once you bring
business objects in Data, you are bound by those business objects (or
interfaces supported by these objects). Why should that be. Let Data
give you what it wants in the format it wants. Then any client can
build business objects the way they want.
 
B

Bruce Wood

That's fantastic. I'm glad that we have an expert in on this
discussion, and I defer to you since you obviously have far more
experience in this area than I have.

However, I have to ask: what's so awful about the OP's original design?
You implied in your original post that it stank and would cause myriad
problems in the future. Yes, I'm sure that it could be much better, and
I'm sure that the frameworks you're talking about are far superior, but
then we're all working at different levels here. What kinds of future
problems will the OP's original design cause? What things could he do
better without spending months or years building complex software
frameworks? I'm not implying that the frameworks you're talking about
are impractical... I'm just wondering how easy they are to implement
for people with limited knowledge and limited time.

As well, in my experience design choices usually hinge on many factors:
project timelines, developer skill, the nature of the particular
business(es) that will use the software (in other words, what is likely
to change in the future and what isn't), and aesthetics. In order to
understand why (or whether) a particular design is good or bad, I need
to understand what are the consequences of choosing that design, and
decide whether those consequences matter in the context of my business.
An excellent design for one context may be vast overkill for another,
while an adequate design in that context may be woefully inadequate in
another.

So, what sorts of the problems can the OP expect in the future if he
just folds the business layer and the data layer into one DLL... or
provides an interface of the kind that I described?

I ask because I'm battling with the same problems here, and could use
some expert advice. :)
 
J

Joanna Carter \(TeamB\)

However, I have to ask: what's so awful about the OP's original design?

Hey Bruce, don't use hyperbole :))
You implied in your original post that it stank and would cause myriad
problems in the future.

The term I used was 'had a bad smell'; this is a phrase used in refactoring
to describe something that doesn't quite feel right.
Yes, I'm sure that it could be much better, and
I'm sure that the frameworks you're talking about are far superior, but
then we're all working at different levels here.

I am not trying to set myself up as the all knowing guru; you should see the
problems I am having getting to grips with generics :))
What kinds of future problems will the OP's original design cause?

When you get classes that reference other classes that reference other
classes that reference the first class, you are starting to knit yourself
some pretty natty spaghetti code. I have consulted for several companies
that have used RAD development tools like Delphi, VB and others, where
developers are encouraged to put all thier code on the forms that they
design. No thought is given to creating standalone code that can be
referenced from more than one form. Soon you have forms that surface methods
and properties that are used in other forms, but the data required for those
methods may be back in the main form. So you get circular dependencies that
severely restrict separation of those forms when a client requires changes
to one or more of those forms. I am assuming the OP is using classes that
are not forms, but the principles are the same.
What things could he do
better without spending months or years building complex software
frameworks? I'm not implying that the frameworks you're talking about
are impractical... I'm just wondering how easy they are to implement
for people with limited knowledge and limited time.

My first question would be: why do you need such a tight interdependency
between classes located in different assemblies ?
As well, in my experience design choices usually hinge on many factors:
project timelines, developer skill, the nature of the particular
business(es) that will use the software (in other words, what is likely
to change in the future and what isn't), and aesthetics. In order to
understand why (or whether) a particular design is good or bad, I need
to understand what are the consequences of choosing that design, and
decide whether those consequences matter in the context of my business.
An excellent design for one context may be vast overkill for another,
while an adequate design in that context may be woefully inadequate in
another.

I would agree with those sentiments. My experience is with companies who
were spending so much time fixing broken code that they didn't have the time
to write good code :-( As with most commercial enterprises, compromise is
not always the easiest attitude to produce good software at low cost. I am
at present completely re-architecting a vertical market application for a
company who do a lot of customisations for their customers. Their original
procedural based program has got so complex that, whenever they add
somethign new, they are very likely to break something else.

Good OO design doesn't have to be complex, just a little better thought out
than the point/click form design that a lot of RAD developers hope to get
away with :))

In the case of the OP, if they can answer the question as to why the close
coupling is thought to be necessary, this may well open the way for a
simpler solution.
So, what sorts of the problems can the OP expect in the future if he
just folds the business layer and the data layer into one DLL... or
provides an interface of the kind that I described?

I suppose the biggest problems that occur are the restriction of having to
use the business classes with the same database, instead of being able to
re-use them in other applications; as well as the extra work that can be
involved when you get a client who just *insists* that you use is legacy
hierarchical database instead of SQLServer.

A well designed OPF can make changing databases so easy that a new
Connection (an OO sort of database driver) can usually be written in less
than an afternoon for most different flavours of SQL.
I ask because I'm battling with the same problems here, and could use
some expert advice. :)

I have only just started to post into the Microsoft groups, but the Borland
groups have a dedicated OO design group, in which we have been discussing
frameworks and layers for some years now. If you care to Google that group
(borland.public.delphi.oodesign) you will find a large resource of questions
and answers from some of the top OO gurus in the Delphi world. Most of the
advice is given in Delphi, but works just as well in C#.

You don't have to 'go all the way' with frameworks to get good results;
refactoring and use of design patterns can slowly transform even the most
ramshackle of code into a much better state :)

Joanna
 
B

Bruce Wood

The term I used was 'had a bad smell'; this is a phrase used in
refactoring to describe something that doesn't quite feel right.

I apologize for the misunderstanding. :)
I am not trying to set myself up as the all knowing guru

You know more than I do... that's good enough for me. However, as you
see that doesn't stop me asking questions. :)

Thanks for the pointer to the Delphi group. I'll watch it.

By the way, have you read the Big Ball of Mud
(http://www.laputan.org/mud/mud.html). It was unfortunately "toned
down" a few years back to appeal more to the academics who hand out the
brownie points (=tenure, =money) at universities, but it's still a
great paper on systems architecture, and why awful design isn't always
awful. :)
 
J

Joanna Carter \(TeamB\)

By the way, have you read the Big Ball of Mud
(http://www.laputan.org/mud/mud.html). It was unfortunately "toned
down" a few years back to appeal more to the academics who hand out the
brownie points (=tenure, =money) at universities, but it's still a
great paper on systems architecture, and why awful design isn't always
awful. :)

Hmmm; and people get paid to do this kind of research? I'm obviously doing
it all wrong!! :))

Joanna
 
N

Nick Malik [Microsoft]

Just for kicks, I used this problem as an interview question about a month
ago, to see if the candidates were familiar with basic techniques in OO
design. One got it right, one got there after a few minutes, and one never
made it. This is a good, common, tough problem that can be solved if
someone has shown you once or twice how to do it.

I would suggest that you take an IoC approach (Google "Martin Fowler
Inversion of Control" for a good paper on IoC).

This means that A2.dll provides some interfaces and, when you create a
particular object in A2, you either pass in a descendent of the interface in
the constructor or in a method call.

My fav example is this: I have a generic logging framework that is useful
for writing audit logs. I configure it using config files. Very general. I
also have a generic configuration handling library that allows me to read
and write config files. Now, I add some features to my config library, and
decide that I want those features to write to the logs. Bingo... circular
reference.

Some folks like seperate assemblies for the interfaces. Other folks like to
put the interface into the base code libraries. Pick one.

The answer is that either the logging library needs to declare that it will
use a config library that meets a particular interface, and have a config
class inherit from that interface, or, in reverse, have the config library
declare that it will use a logging library that meets a particular
interface, and have a logging class inherit from that interface. Once
again, pick one (or, if you like Aspect Oriented Programming... pick both
:).

Then your calling app will create the config object, and either pass in no
logging library, or pass in a "dummy" logging library that meets the
interface requirements but doesn't log anything.

Next, your calling app will create the logging object, passing in the config
object in the constructor.

Lastly, your calling app will invoke a method on the config object to pass
in the logging library.

Of course, this may not be the easiest thing in the world to understand. I
hope it helps.

--
--- Nick Malik [Microsoft]
MCSD, CFPS, Certified Scrummaster
http://blogs.msdn.com/nickmalik

Disclaimer: Opinions expressed in this forum are my own, and not
representative of my employer.
I do not answer questions on behalf of my employer. I'm just a
programmer helping programmers.
--
 
B

Bruce Wood

Having spent more than my fair share of years in academia, I can tell
you that that paper was a stroke of genius and a breath of fresh air.
Many (most?) academics consider business programmers to be idiots who
can't think their way out of wet paper bags, because they don't
implement the latest fashion in system architecture. It was refreshing
to hear a couple of guys saying that business programmers do these
things for good reasons, not just out of ignorance. Unfortunately, as I
said, the paper was "toned down" and made more palatable to the
academic crowd in a revision several years back. It now injects a bit
more of the "people do this because they're unenlightened" tone into
the text, probably to make some political masters happy. However, the
basic idea--that people who take short cuts don't do it because they're
dolts--is still a unique perspective in the academic world, in my
experience. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top