问题
I'm writing an application where I need to simulate the distribution of a resource such as electricity or gas among a large network of objects connected to each other in arbitrary ways. No physics need to be simulated. Just the flow of the units of resources when objects ask for them.
Consider the below diagram:
ObjA <---> ObjB <---> PwrA <---> ObjF
/\ ObjG
ObjE<---/ \---> PwrB
The Pwr objects can provide resource electricity when asked by the Obj objects. What I need to simulate here is that if ObjA needs 50 electricity it should send a message to all connected peers without caring who it goes to and should get the resource back only from peers it is connected to directly or indirectly.
I'm not simulating latency here or anything so transmission will be instant. From a programming point of view this is all entirely local within the application (No real networking or anything like that).
My challenge here is trying to program this in a modular and clean way. I'd like to avoid giant lists of locally connected nodes and large ifelse statements about how to deal with messages if possible.
I want to both achieve my goal and also learn something new. I've been reading articles and books and programming concepts for a few weeks now and one that I think might be a fantastic solution is C# Delegates.
I've done some prototyping and managed to setup objects with a MessageOut Delegate that other connected objects can subscribe to and listen for outgoing messages to deal with.
This works quite well but I have the following short-comings due to my in-experience with the concept:
- I don't know how to deal with circular references with message passing between connected objects: In the above diagram if ObjB broadcasts a message for electricity ObjA, PwrA, PwrB, ObjE will be notified, which will in turn all process and then notify ObjB. Which will in turn notify...etc. The way networking deals with this is to simply broadcast out all ports (Objects listening to a delegate) except the port that received it. How can I implement this with delegates? A flat broadcast won't work.
- Listening to a delegate effectively creates one-way communication. To implement two-way communication I'd need to have objects listen to outgoing messages for each other. But without a above to the above its another circular reference surely?
回答1:
Delegates would be unwieldy and messy in my opinion.
Have you looked at TPL Dataflow? http://msdn.microsoft.com/en-gb/library/hh228603.aspx
It provides a number of "blocks" that have behaviour such as queuing requests, sending requests etc, you could use it to simulate the push and pull of these virtual resources.
All the magic for connecting them together and dealing with circular dependencies is all taken care of.
回答2:
If you're working with events, you will notice the best practice is to have a "sender" object and an "EventArgs" in every event delegate.
I had to do something like that once, and the solution that came up was adding the "OriginalSender" into an EventArgs derivate class.
Every received message passes first into an if statement:
class MyEventListenerClass
{
.....
private void EventHandlerMethod(object sender, MyEventArgs E)
{
if (E.OriginalSender != this)
{
do what is needed
}
else
{
got this message from myself in a circular way....discard it
}
}
......
}
class MyEventArgs : EventArgs
{
public object OriginalSender {get; private set;}
public MyEventArgs(object OrigSender) : base ()
{
OriginalSender = OrigSender;
}
}
But that doesn't solve everything. In my case, there weren't any loops not containing the original sender. If the messages fall into a loop like that, it will be infinite. So maybe a list of senders could solve it, making if (E.SenderList.Contains(this)) {...}
.
So every time a message is received, the receiver checks if the list contains itself. If not, he sends the message adding itself into the senderlist.
回答3:
Seems that you in the right track .. it is actual lay called the observer/listener design pattern ... and you should use Events in C# to better implement that pattern. Events are basically a type of delegate you can find more info in MSDN or Google...
With regards to the circularity problem:
You shield just pass a list of senders along with the message .. and each object that listiens will have to check if he was one of the senders i n that list and decide weather he should transmit a Massey as well or not.
回答4:
I don't think that delegates are a good solution for the problem you presented. Lets take a look at the following scenario:
ObjB needs 1 electricity unit, now, let's assume that it have a delegate called "GetElectricity", as i understand from what you wrote, PwrA, PwrB have both added an handler for the delegate, both PwrA and PwrB handlers will be invoke, or you will try the first handler, if it didn't provided that electricity needed it will try the next one... this will create a complex, hard to maintain software.
what you are talking about (or at least, what i would do :)), is Events, ObjB will raise an event for NeedElecricty, all the Pwr objects have register for it, if they can provide elecricity they will call an event "ProvideElectricity" on ObjB, than ObjB can decide from which one of them he will take the power he needs, and inform them that he needs 1 power unit from PwrA (and will tell PwrB that he doesn't require it services).
look for Observers and Events, as it is what you are mainly talking about.
I agree with @Clint, the TPL dataflow is the best solution, as it does exactly what you are talking about. but as you said it is out of scope.
来源:https://stackoverflow.com/questions/15787263/using-delegates-to-simulate-connected-objects