|
(NOTE: I've been programming for over 20 years so please be gentle if I'm just missing something right under my nose)
In C++, it was common to have situations where a method definition would need something like...
void CSomeClass::DoSomething(CAnotherClass* pObject)
{
if (pObject) {
}
}
You could simplify your world by defining the method so that it took a reference to an object...
void CSomeClass::DoSomething(CAnotherClass& object)
{
}
Passing in a reference to an object eliminated the need to put ASSERT statements and/or runtime checks in a ton of locations that would have required it such as when using pointers. I'm finding that with C#, similar designs using reference types are requiring asserts and runtime checks all over the frackin' place.
Someone please tell me there is a way to eliminate all these checks for null references in C# like I could in C++.
|
|
|
|
|
You can either require all parameters to be passed in by ref , which is not too good a practice, or you could use something like Code Contracts to ensure it's not null.
|
|
|
|
|
ref to a reference type is too much like a pointer to a pointer. However, this brings up a question I've had while trying to use C# on occasion for small projects to learn it. Are there equivalent const rules in C# (as acompared to C++) to help prevent the method from modifying the "pointer"?
If there were something like that, ref might not be so bad since it would be a performance increase (at least in theory since I wouldn't need the runtime checks on the reference.
Pete O'Hanlon wrote: which is not too good a practice
My knee-jerk reaction is to agree but if it would save the runtime checks, I'd be game to study the idea more.
None of my books really get into ref with reference types too much except the usual drive-by introduction. The best I've found is Richter's book but he basically says it's like a pointer to a pointer so I'm back to looking for a const solution like I would in C++. I'm not sure, but it seems const isn't really as much of a "thing" like it is in the C++ world.
I'm not familiar with "Code Contracts" so I'll give it some research time.
thanks.
|
|
|
|
|
bob16972 wrote: Are there equivalent const rules in C# (as acompared to C++) to help prevent the method from modifying the "pointer"
No. This has been one of the features I've bemoaned the absence of the most.
|
|
|
|
|
oo ouch. Design by contract...
"When using contracts, a supplier should not try to verify that the contract conditions are satisfied; the general idea is that code should "fail hard""
Hmmm. To be fair, I guess I use this everyday (in an indirect way) now that I think about it since most class libraries seem to conform to this (at least the "fail hard" part). However, I don't think that would work well for me and my pieces of the puzzle since not catching an exception will most likely result in the same behavior as a runtime error. I tend to feel exceptions are over used anymore but I try to keep an open mind about that subject.
|
|
|
|
|
bob16972 wrote: are requiring asserts and runtime checks all over the frackin' place
I suppose that depends on what you mean by "all over the ...place" and what exactly your intent is.
In C++ I put parameter validation checks in public API methods.
In C# I put parameter validation checks in public API methods.
In either I might put some validation checks in other locations, but rarely do.
The API would decide what sort of parameters are needed - not whether I need to check it or not. Especially since in C++ it is possible to pass a reference to a pointer.
Your example code however doesn't suggest a parameter validation check but rather optional behavior it the parameter is null. That of course must be dealt with regardless.
Finally null pointer exceptions in C# have stack traces on them. So arbitrary programming errors show up that way. Especially with sufficient unit testing.
|
|
|
|
|
jschell wrote: I suppose that depends on what you mean by "all over the ...place" and what exactly your intent is.
All over the place meaning, I use objects (all over the place) which require references which can be null. Any internal, public, protected methods can be called by code that does not necessarily know my assertions inside the method. Because of this, all references that could be null need to be checked (private methods can sometimes omit this as I stated) before they are used (unless it's just passing through of course).
jschell wrote: In either I might put some validation checks in other locations, but rarely do.
Good luck with that.
jschell wrote: Finally null pointer exceptions in C# have stack traces on them. So arbitrary programming errors show up that way. Especially with sufficient unit testing.
That's assuming your debug sessions will take a particular code path; unit tests can get stale as code changes. Since this might not be reliable, the runtime checks are usually the last stand to prevent runtime errors and replace them with graceful hickups or alternative behavior.
This coding style is MFC 101 and can be applied where ever pointers are sold. Since the C# references can be null, this approach almost appears mandatory but that is the question at hand.
|
|
|
|
|
bob16972 wrote: Good luck with that.
Been programming 40 years myself. And my delivered software tends to have much lower bug counts compared to peers.
It has nothing to do with luck and everything to do with attention to detail. Assertions "all over the place" would not help with that.
Should note as well that I can't recall the last time I had to deal with a null pointer reference in my code. I can however remember dealing with them in other peoples code.
bob16972 wrote: That's assuming your debug sessions will take a particular code path
Code coverage tools. Great investment.
bob16972 wrote: unit tests can get stale as code changes.
That is a process problem not a technology problem.
And the unit tests on the code that I maintain do not get stale.
bob16972 wrote: Since this might not be reliable, the runtime checks are usually the last stand to prevent runtime errors and replace them with graceful hickups or alternative behavior.
The behavior of asserts do not fall into that categorization.
You can write a class easily in C# and C++ for that matter that provides a rudimentary Aspect programming context for checking a modest number rules including null pointers.
bob16972 wrote: This coding style is MFC 101 and can be applied where ever pointers are sold.
Pointer bugs which occur in C++ do not apply to C# (nor to java for that matter). A system OS exception is only one possibility that might occur due to a C++ pointer bug and is actually one of the easiest to locate and fix.
Other than that having written C++ for 20 years I got by quite comfortably without scattering asserts all over my code - and again with a much lower bug rate for my code base that others.
bob16972 wrote: Since the C# references can be null, this approach almost appears mandatory but that is the question at hand.
And perhaps you would be better off with an Aspect type language. A quick google suggests that that exists in some form for C# from different sources. Java has AspectJ which is a full Aspect language. Something similar might exist for C#.
|
|
|
|
|
bob16972 wrote: Someone please tell me there is a way to eliminate all these checks for null references in C# like I could in C++.
Hm, there is nothing like "null reference" in a C++ program. This would require dereferencing a null-pointer which immediately results in undefined behaviour of the rest of the program. If you pass an object by reference in C++, you implicitly state that this object cannot be a reference to null object by definition.
In managed code you can use explicit code contracts. CCs allow you to make explicit assumptions about pre-, post- or state-conditions which are verifiable at compile- and run-time. You formulate contracts at metadata level so they get injected into IL in AOP manner:
[ContractClass(typeof(ISomethingContract))]
public interface ISomething {
public void DoSomething(AnotherClass pObject);
}
[ContractClassFor(typeof(ISomething))]
internal abstract class SomethingContract : ISomething {
public void DoSomething(AnotherClass pObject) {
Contract.Requires(pObject != null);
}
}
}
|
|
|
|
|
That's because C++ references are syntactic sugar to make C++ easier for VB developers who wander over.
|
|
|
|
|
Hey now. I like C++ references and I despise VB with a passion.
|
|
|
|
|
Hi.
I don't think that passing as a reference really gets rid of the need for null checks. You can satisfy the rule for an actual object by dereferencing a pointer. That satisfies the compiler but the pointer could still be NULL. Consider this working C++ code.
void NameList::Append(NameList *ToBeAdded)
{
Members->Append(*ToBeAdded->Members);
ToBeAdded->Members->RemoveAll();
} where "Members" is defined as
CObArray *Members and the CObArray member Append is defined as
INT_PTR Append(const CObArray& src); so if the "ToBeAdded" Namelist Object has a Members field that contains NULL, the compiler is OK with that and the Append function should really do ASSERT and/or runtime checks.
In any event, I don't think you can rely on the & to protect against somebody passing NULL.
Chuck (45 years coding and counting)
|
|
|
|
|
Are you referring to what is described in the C++ Specification, Section 8.3.3, page 179 ?
"A reference shall be initialized to refer to a valid object or function. [ Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior. As described in 9.6, a reference cannot be bound directly to a bit-field. —end note ]"
Your point is taken but I think in the case you described, the violation occurs by the caller and before the prolog of the method completes. If the caller violates the standard definition of a reference, there is nothing I can do about it. I'm not even sure there is a way to check the reference for validity since the result of the action you described would be undefined (or at least, so it seems).
|
|
|
|
|
Well, I was simply reacting to your statement in the original post,
In my experience, when some code assumes things cannot be null, that's exactly where to start looking for bugs. And if you're assuming that a "by reference" specification removes some level of bug enough that you can remove the ASSERT / checks, then I wanted to show an example (real, not concocted) that shows the assumption to be false.
And then you asked for a way to eliminate such checks from C# code like you could from C++ code, which I infer to mean that you have such checks, find them somewhat inefficient and/or redundant in well behaved programs, and would like to remove them using some help from the language. Again, I wanted to point out that the C++ language does not give you any help either and that your trust in it was misplaced.
Now your reply says
bob16972 wrote: the violation occurs by the caller and before the prolog of the method completes. If the caller violates the standard definition of a reference, there is nothing I can do about it. I'm not even sure there is a way to check the reference for validity since the result of the action you described would be undefined (or at least, so it seems).
which seems like an reasonable argument to remove all checks anyway since there's nothing you can do when the caller violates some rules, including the assumed rule that they'll pass something reasonable.
Look, all I'm saying is that if you are writing public API entries, for other programmers to call, you're better off including the checks all the time. Yes, "null pointers" are just one element in the set of all possible bad arguments and checking for them will never catch the case of the pointer being 0x00000001 or 0xcdcdcdcd (assuming 32-bit pointers) but ASSERTs may catch enough during debugging that may make debugging easier.
|
|
|
|
|
I agree that if a legal value can be null, always check it first. However, for a C++ reference, the rules don't provide for a legal null value so its not a valid option for the caller.
In C++, by definition, you cannot have a null reference. If the caller tries to do anything that dereferences a null pointer, they have violated the rules and will probably crash the program before the function or method call. What you are proposing is that I can somehow protect against the caller crashing the program before they even enter the method or function.
Think about it. your suggesting the caller can access memory location NULL, which for sake of argument is 0 in MFC, which results in an attempt to get the value at virtual memory location 0. In windows, attempting access any virtual memory address between 0-65535 will cause a violation. My function or method would not even be involved at that point so I would hope you would grant that there is no need to protect against something that can never get past the threshold of my functions prologue.
If you won't grant me that argument, then I'd be interested to hear the rebuttal as I don't see how it could get into my function in the first place before being stuffed by the OS.
Regardless, thanks for the comments as they have been thought provoking and interesting and always welcome.
|
|
|
|
|
I'm afraid you missed a subtle point in my example, the point that gets you into your function with a NULL value. I've simplified it below
void Bar(CObArray &ar)
{
int foo = ar.GetCount();
}
void Foo(void)
{
CObArray *bob = NULL;
Bar(*bob);
} set a breakpoint on the "int foo = ar.GetCount()" and observe that ar cannot be examined in the debugger, it's value is null. Note that you do in fact get to that statement, that is, the "Bar(*bob)" in the function "Foo" does not throw an exception. Although it looks like it is dereferencing the clearly null pointer, it's really just creating the argument for Bar by getting the address of the CObject and passing it, that is, making a "by reference" value for the Bar function. Therefore, the "crash" happens in "Bar" (i.e. your function) rather than at the invocation site.
In my first reply, I was showing that the "ToBeAdded" object, which was valid, had a "Members" data member that was, in fact, NULL and that there was no "help" from the compiler / language rules that prevented it from passing NULL, even though the rules appear to say that's illegal.
So, my conclusion was that if you don't want to allow NULL as an input to your functions and that you used to explicitly ASSERT / check for that case, you need to continue to check for that case because simply using "&" to eliminate that possibility was not going to work.
Thus ends my rebuttal
|
|
|
|
|
What you describe is Microsoft (implementation) specific version of "undefined behavior". The code in Foo is ill-formed and any assumptions about what happens after you took a reference to a "null" pointer are worthless...(the lang. spec. does not require an exception to be thrown.)
|
|
|
|
|
Well, that is true to some extent (the undefined behavior part, not the malformed part )
Unfortunately, I don't live in the happy world where I can rely on everybody following the rules or where I can hold up a standard and claim it's not my fault that my code blew up.
I live in a world, somewhat dominated by Microsoft whose implementation details have real effects, where my libraries, reusable modules, etc are to be used by other programmers and they will often have bugs. I try my best to prevent their bugs from crashing my code but instead give them a reasonable exception or other error indicator so they can find their own bugs without calling me to say "your code crashed".
Perhaps this is ingrained in me from my early years of operating system development where a kernel crash was the worst possible thing regardless of what the "user" did to it via the APIs. Imagine how angry people would get if simple calls to the base Windows APIs would cause blue screens all the time.
paul_71 wrote: any assumptions about what happens after you took a reference to a "null" pointer are worthless
And this is exactly my point to the OP, his assumption that one cannot get into his code with a null object reference by using "&" is also worthless (your words) as I can clearly demonstrate that you can.
In any event, I will continue to challenge the assertion that one can remove defensive code because "by definition it cannot happen".
|
|
|
|
|
PS, it seems they've been arguing about this exact syntax for quite some time now. it appears there are two camps in the standard, one that wants it be defined behavior and one that does. The following like should provide some interesting reading on the subject.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
|
|
|
|
|
Ouch. I stand corrected. I started the day sure the sky was blue and now I'm left wondering if their implemetation ignores the statement "must be initialized by a valid object" and the part on page 198, section 8.5.3...
"...Argument passing (5.2.2) and function value return (6.6.3) are initializations."
But I agree (this includes some of your statements in another path of this thread as well), that I'm stuck with their implementation. I noticed they even let me dot off the invalid reference and call methods without any exceptions. At first I was shocked, but now I'm quite intrigued to see if any compiler vendor can implement this correctly.
I'm glad I never had to go up against you in debate class.
Excuse me while I go fix a million lines of active code.
|
|
|
|
|
Hm, not sure to whom this may concern, I always get confused by these hierarchies of threads... Anyway, the concept of a C++ reference is a subtle thing and it got even subtler with c++0x, as you might know. For good reasons this concept wasn't considered is what C# and other CLR targeting languages - and now we're back at OP's problem (at least as I see it...): in case of reference types (and in a verifiable program), you always deal with handles ("pointers") to instances. These handles can have two states: (a) pointing to valid object, (b) or "null". You can take a value of a "handle" by reference, by stating this intent explicitely at both call and target sites. However, this does not change the semantics of a handle, you just get access to the location of whatever the handle represents. So in C#, if you want to protect against handle pointing to "null", you either have to be explicit about that (code contracts are a great way to do that), or you rely on NullArgumentException handling (which is guaranteed to be thrown by the specs). The ref modifier mentioned somewhere in the thread does not help much:
void Bar(MyClass pParam) {
pParam.Member();
}
void Bar(ref MyClass pParam) {
pParam.Member();
pParam = new MyClass();
}
void Foo() {
MyClass tI1;
MyClass tI2 = null;
MyClass tI3 = new MyClass();
Bar(ref tI1);
Bar(ref tI2);
Bar(ref tI3);
Bar(tI1);
Bar(tI2);
Bar(tI3);
}
|
|
|
|
|
bob16972 wrote: If the caller violates the standard definition of a reference, there is nothing I can do about it. I'm not even sure there is a way to check the reference for validity since the result of the action you described would be undefined (or at least, so it seems).
Exactly, with no need to further qualify the statement as you did in the second sentence. And no, there is no way to check a reference for validity, since it must be valid by definition. Otherwise, the caller has violated (a) the language specification of a valid C++ program and (b) the contract you have specified by defining the method signature that way... That's what I wrote in my post above (which was down-voted for some reason).
|
|
|
|
|
Jeez. You missed the whole point of what I'm trying to do. You keep focusing on C++ references and pointers whereas I have no problem with C++ since I can choose to use pointers when I want to allow null and a reference when I don't. In C# sharp, I don't seem to have that option (except for ref apparently which would require a const to (attempt to) protect the caller from the method changing the reference on them.
From reading your earlier post, you apparently didn't read the code comments in my code examples as I had already stated that a reference can't be null but you tried to explain that very thing to me which would indicate you missed the point and the question.
Regardless, I appreciate your attempt to help find a solution, for what it's worth.
thanks.
|
|
|
|
|
Hello, I have a winform project in C# which I added a reference to windows media player.
when Im in the project properties I get the error:
An error occurred trying to load the page.
Error HRESULT E_FAIL has been returned from a call to a COM component.
Why is that?
The project is working fine including the player.
--Edit--
I found this error also but I didn't understand its meaning:
COM Reference 'WMPLib' is the interop assembly for ActiveX control 'AxWMPLib' but was marked to be linked by the compiler with the /link flag. This COM reference will be treated as a reference and will not be linked.
modified on Friday, January 14, 2011 2:30 PM
|
|
|
|
|
Hi,
Our solution in .net 3.5 has a number of dll's as output. In those dll's we populate some fields like version, company, product etc. But also a copyright notice. For instance, if I have this notice: "Copyright (c) 2011 Initech Inc.".
Is it possible to make the year value be determined at compile time? So, if I compile it in 2011, then it says "Copyright (c) 2011 Initech Inc.". If I compile it in 2012, it would say "Copyright (c) 2012 Initech Inc.".
I've toyed with some things, but AssemblyCopyright requires a constant as value.
Is it possible at all?
The consumer isn't a moron; she is your wife.
|
|
|
|
|