Reputation: 2285
Consider the following issue
When designing a framework, an interface exposing some event is presented
interface I
{
event MyEventHandler MyEvent
}
This interface will eventually be implemented by many different 3rd party vendors, and may be consumed by various clients.
Since each vendor may new up the event args with invalid data, the only control I have as a framework author is at the event args level, so I thought of the following pattern:
class MyEventArgs
{
public int? MyProperty{get;}
MyEventArgs(int arg)
{
if(arg.IsInvalidArgument())//Let's pretend that there's such an extension method
throw new ArgumentException(...)
MyProperty = arg;
}
This ensures that a client can not use invalid values provided by some rogue piece of code, since the constructor throws an exception, hence the integer will have no value assigned, making it a null reference.
However, this also creates overhead in client code, since now the client has to check the HasValue and then access Value, making the EventArgument less user friendly.. This becomes even more cumbersome when the amount of parameters per event argument grow.
I could technically remove the question mark, which would rid the client of the Nullable nonsense, since in my opinion there is no way on god's green earth to obtain a reference to such an instance, but the problem is that this scenario, although easy to test, may have edge cases which I never thought of, hence my question.
Is there any possible way to obtain a reference to an instance whose constructor had thrown an exception and pass it to the event listeners?
Upvotes: 3
Views: 232
Reputation: 171178
When a constructor throws an exception there is no way to obtain a reference to that object (except if the object cooperates by handing out this
before throwing; not likely, bad design).
For that reason your invalidly valued object is unreachable. It's state is indeed invalid (default initialized) but nobody can see it. It's like a virtual machine that is corrupted on the inside and tries to launch missiles but you have disabled the virtual NIC.
This pattern is used all over the place. You have used it many times without realizing. For example, if you say new FileStream(null)
how would you obtain a reference to that invalid stream? You can't.
Just do the normal thing. It's good that you thought this through, though.
Is there any possible way to obtain a reference to an instance whose constructor had thrown an exception and pass it to the event listeners?
No. But here's an example where it's possible:
class C {
public static C Instance;
public C() {
Instance = this; //publish/leak
throw ...;
}
}
Just don't do that. This is unnatural code anyway. The constructor of an object normally should not do much, it should bring the object into a valid state and not cause side-effects. Side-effects are the only way to publish a reference to this
.
There's one more catch: A finalizer will be invoked on that object if one is present. Finalizers are rare because because most of the time unmanaged resources should be held by handle classes. For that reason this issue rarely comes into effect. But a finalizer can see the private state of its object. Make sure that it can deal with that. FileStream
s finalizer will probably check for aborted initialization and do nothing.
Upvotes: 3
Reputation: 488
To answer your question. No. you can't have a null result if an error is encountered in a constructor
However I think the best possible way around this is to add a static constructing function to the MyEventArgs class
public static int DEFAULT_ARG = 1;//you can set this to whatever you want
public static Create(int arg)
{
if(checkArg(arg))
{
return new MyEventArgs(arg);
}
return new MyEventArgs(MyEventArgs.DEFAULT_ARG);
}
Then instead of using the constructor directory using var event = new MyEventArgs(arg);
you use var event = MyEventArgs.Create(arg);
It's a bit of a stress using this method on multiple eventargs but hey :) you could always derive all of them from the same generic abstract class
Hope this helps :)
Upvotes: 0
Reputation: 151594
the client has to check the
HasValue
and then accessValue
Well it's not necessary to make the property a Nullable<int>
, as you can only set it from the constructor (assuming an omitted private set
), where it is non-nullable.
Is there any possible way to obtain a reference to an instance whose constructor had thrown an exception
No, when a constructor throws an exception, you don't get an instance back.
Upvotes: 0