|
I am sorry you do not fully understand what I wrote.
First, I did show that constructor exceptions are "swallowed", though I do admit that is a subjective term. To be more precise, constructor exceptions occur outside the MSIL exception handling. In order to catch the constructor exception, the "using" has to be wrapped in a try-catch of its own.
Second, because of what I just explained, the unit test that implements "using" needs the outer try-catch, while the other approach does not. The "using" approach results in nested try-catches, which is why it creates more lines of code to be executed. The coding for the unit tests is correct "as-is". The outer try-catch in your version of the StandardUsageTest() method is unnecessary.
Third, the execution time is not the point. I ran them several times, to account for any caching, and the time relationship remains the same. The point is that a very popular C# coding shortcut results in more lines of code to be executed (MSIL) than the other approach, and forecloses the opportunity to capture runtime values in the exception's Data dictionary within the MSIL code.
If you want to stick with the "using" statement, then do so. I am not trying to tell you or anyone else what they should do. I am presenting information for the "what" and "how" so that an objective person can decide for themselves. I provided the details, as was asked, and it completely and rationally supports my thesis. If you agree, fine. If you disagree with rational reasons you can explain, then educate us all. But since you are disagreeing without a rational basis, that is fine. You should continue to do things the way you think is best. But if you don't like my approach, just admit it is a personal preference to stick with the "using" statement. For example, if benefitting from more advanced exception handling does not provide a value for you, then implementing the "using" statement makes sense.
|
|
|
|
|
MSBassSinger wrote: To be more precise, constructor exceptions occur outside the MSIL exception handling. In order to catch the constructor exception, the "using" has to be wrapped in a try-catch of its own.
OK, that makes more sense now.
However, unless you specifically need different catch blocks for exceptions thrown from constructing the class and exceptions thrown from using the class, you can still get away with a single try..catch block in your code; it just needs to wrap the entire using block.
[TestMethod]
public void UsingStatementTest()
{
try
{
using (DisposableClass test = new())
{
String result = test.WhoIsIt();
}
}
catch (Exception exOuter)
{
Assert.Fail($"ONLY: {exOuter.Message}");
}
}
You'll still have the try..finally from the using block nested within the try..catch block from your own code. But as you can see from the MSIL of your own StandardUsageTest method, a try..catch..finally block is implemented as a try..catch block wrapped in a try..finally block.
The IL, for comparison:
1 .method public hidebysig static
2 void StandardUsageTest () cil managed
3 {
4
5
6 .maxstack 3
7 .locals init (
8 [0] class DisposableClass test,
9 [1] string result,
10 [2] class [System.Private.CoreLib]System.Exception exOuter
11 )
12
13 IL_0000: nop
14 IL_0001: ldnull
15 IL_0002: stloc.0
16 .try
17 {
18 .try
19 {
20 IL_0003: nop
21 IL_0004: newobj instance void DisposableClass::.ctor()
22 IL_0009: stloc.0
23 IL_000a: ldloc.0
24 IL_000b: callvirt instance string DisposableClass::WhoIsIt()
25 IL_0010: stloc.1
26 IL_0011: nop
27 IL_0012: leave.s IL_0034
28 }
29 catch [System.Private.CoreLib]System.Exception
30 {
31 IL_0014: stloc.2
32 IL_0015: nop
33 IL_0016: call class [System.Private.CoreLib]System.IO.TextWriter [System.Console]System.Console::get_Error()
34 IL_001b: ldstr "ONLY: "
35 IL_0020: ldloc.2
36 IL_0021: callvirt instance string [System.Private.CoreLib]System.Exception::get_Message()
37 IL_0026: call string [System.Private.CoreLib]System.String::Concat(string, string)
38 IL_002b: callvirt instance void [System.Private.CoreLib]System.IO.TextWriter::WriteLine(string)
39 IL_0030: nop
40 IL_0031: nop
41 IL_0032: leave.s IL_0034
42 }
43
44
45 IL_0034: leave.s IL_0045
46 }
47 finally
48 {
49 IL_0036: nop
50 IL_0037: ldloc.0
51 IL_0038: brtrue.s IL_003c
52
53 IL_003a: br.s IL_0043
54
55 IL_003c: ldloc.0
56 IL_003d: call instance void DisposableClass::Dispose()
57 IL_0042: nop
58
59 IL_0043: nop
60 IL_0044: endfinally
61 }
62
63 IL_0045: ret
64 }
1 .method public hidebysig static
2 void UsingStatementTest () cil managed
3 {
4
5
6 .maxstack 3
7 .locals init (
8 [0] class DisposableClass test,
9 [1] string result,
10 [2] class [System.Private.CoreLib]System.Exception exOuter
11 )
12
13 IL_0000: nop
14 .try
15 {
16 IL_0001: nop
17 IL_0002: newobj instance void DisposableClass::.ctor()
18 IL_0007: stloc.0
19 .try
20 {
21 IL_0008: nop
22 IL_0009: ldloc.0
23 IL_000a: callvirt instance string DisposableClass::WhoIsIt()
24 IL_000f: stloc.1
25 IL_0010: nop
26 IL_0011: leave.s IL_001e
27 }
28 finally
29 {
30
31 IL_0013: ldloc.0
32 IL_0014: brfalse.s IL_001d
33
34 IL_0016: ldloc.0
35 IL_0017: callvirt instance void [System.Private.CoreLib]System.IDisposable::Dispose()
36 IL_001c: nop
37
38
39 IL_001d: endfinally
40 }
41
42 IL_001e: nop
43 IL_001f: leave.s IL_0041
44 }
45 catch [System.Private.CoreLib]System.Exception
46 {
47 IL_0021: stloc.2
48 IL_0022: nop
49 IL_0023: call class [System.Private.CoreLib]System.IO.TextWriter [System.Console]System.Console::get_Error()
50 IL_0028: ldstr "ONLY: "
51 IL_002d: ldloc.2
52 IL_002e: callvirt instance string [System.Private.CoreLib]System.Exception::get_Message()
53 IL_0033: call string [System.Private.CoreLib]System.String::Concat(string, string)
54 IL_0038: callvirt instance void [System.Private.CoreLib]System.IO.TextWriter::WriteLine(string)
55 IL_003d: nop
56 IL_003e: nop
57 IL_003f: leave.s IL_0041
58 }
59
60 IL_0041: ret
61 }
SharpLab[^]
Practically the same number of lines for both.
The one advantage I can see to your method is that, assuming the constructor hasn't thrown an exception, the instance will be available and not disposed-of in your catch block. With the using block wrapped in a try..catch block, the instance will already have been disposed of, and the variable will be out of scope, by the time you reach the catch block.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
If it actually matters, then...
Widget w = new Widget();
using ( w ) ...
|
|
|
|
|
Even VS 2022 does this. I hate it & its not the correct way to be writing anything in C#.
|
|
|
|
|
|
Edit: My husband had to explain to me that "DOCTOR OF PHILOSOPHY" is what you write on all doctoral dissertations, regardless. This is what I get for not ever pursuing a degree. You may commence with the public shaming now.
I stumbled upon this on the internet while looking for information on LL(k) parsing
GENERAL CONTEXT-FREE RECOGNITION AND PARSING BASED ON VIABLE PREFIXES
By
D. CLAY WILSON
A DISSERTATION PRESENTED TO THE GRADUATE SCHOOL OF THE UNIVERSITY OF FLORIDA IN PARTIAL FULFILLMENT OF THE REQUIREMENTS FOR THE DEGREE OF DOCTOR OF PHILOSOPHY
UNIVERSITY OF FLORIDA
Philosophers are doing this stuff? Really? This is some heavy math and logic. Now, I can see it from the perspective of demonstrating a grasp of the principles of logic, but this seems like overkill, especially given the math involved! This is something I expect of a Doctor of Computer Science, not a Philosophy PhD.
Real programmers use butterflies
modified 28-Nov-21 12:15pm.
|
|
|
|
|
And no smiley?
(I take for granted that you know the meaning of "PhD".)
|
|
|
|
|
Yes, but it's early so you'll have to forgive my transgression.
moar coffee. (edited)
Real programmers use butterflies
|
|
|
|
|
Wait a minute, are you saying this dissertation is probably for a CS degree?
Real programmers use butterflies
|
|
|
|
|
trønderen wrote: I take for granted that you know the meaning of "PhD".
Piled higher and deeper?
Freedom is the freedom to say that two plus two make four. If that is granted, all else follows.
-- 6079 Smith W.
|
|
|
|
|
"PhD" stands for Doctor of Philosophy and is a generic term used in many disciplines, including Computer Science, for doctoral degrees. Some universities used ScD (Doctor of Science) instead.
|
|
|
|
|
I meant to put PhD after philosophy, not computer science. it's early here and I haven't had enough coffee. I'm fixing that. (Edited since)
Edit: Wait. I think I misunderstood you. Are you saying this dissertation was probably for a Comp Sci doctorate?
Real programmers use butterflies
|
|
|
|
|
Yes, almost certainly for a Comp Sci doctorate.
|
|
|
|
|
Yeah my hubby had to explain how this worked to me.
Real programmers use butterflies
|
|
|
|
|
I'm glad that got sorted so there won't be any confusion when some university calls to give you an honorary one!
modified 29-Nov-21 7:01am.
|
|
|
|
|
There are no philosophers in Florida. The alligators ate most of them, and the rest realized that FL is not the place to live if you are practicing philosophy.
|
|
|
|
|
You receive a Doctorate of Philosophy when presenting new work proving that you can extend science. It is not considered science though when you submit it. It becomes science when it is peer-reviewed and has gone through the other procedures that get it accepted into what is considered the body of science. Until then, your work is considered philosophy.
|
|
|
|
|
Just remember when dealing with thesis papers:
BS => Bull S.
MS => More of the Same
PhD => Piled Higher and Deeper
|
|
|
|
|
On a serious note, I admire people who can own their gaffs proudly. Much respect.
cheers
Chris Maunder
|
|
|
|
|
A little late to disguise myself and slink away at this point anyway. You people recognize me.
Real programmers use butterflies
|
|
|
|
|
|
I'm reverse-engineering some code, and I've found this as the "decryption" for some data:
unsigned char encrypted[...];
unsigned char decrypted[sizeof(encrypted)];
for (int i = 0; i < (int)sizeof(encrypted); i += 4)
{
DWORD temporary = ((((DWORD)encrypted[i + 0]) << 24) & 0xFF000000) |
((((DWORD)encrypted[i + 1]) << 16) & 0x00FF0000) |
((((DWORD)encrypted[i + 2]) << 8) & 0x0000FF00) |
((((DWORD)encrypted[i + 3]) << 0) & 0x000000FF);
temporary = temporary * 487709855;
decrypted[i + 0] = (BYTE)((temporary & 0xFF000000) >> 24);
decrypted[i + 1] = (BYTE)((temporary & 0x00FF0000) >> 16);
decrypted[i + 2] = (BYTE)((temporary & 0x0000FF00) >> 8);
decrypted[i + 3] = (BYTE)((temporary & 0x000000FF) >> 0);
} I remember seeing this sort of thing before, but I don't remember what it's called. Any suggestions?
Software Zen: delete this;
|
|
|
|
|
Obscurity?
Edit, how about:
Affine cipher - Wikipedia[^]
Further edit:
If it is Affine, is it Quadruple-Affine? Effectively using a different key for each of four bytes all at once?
modified 24-Nov-21 16:59pm.
|
|
|
|
|
I don't know its name, but I recognise the algorithm.
It's big-endian 32bit, with the encryption and decryption keys being multiplicative inverses mod 2^32.
On a big-endian machine, the loop becomes something like
for (int_32 i = 0; i < sizeof(array); i++)
{
array[i] *= magic;
} where (encrypt_magic * decrypt_magic) % (1<<32) == 1
As an encryption algorithm, it's nuisance value only. But I have used it a few times just to discourage tampering.
Cheers,
Peter
Software rusts. Simon Stephenson, ca 1994. So does this signature. me, 2012
|
|
|
|
|
Thanks for the information. I recognized the form, but couldn't remember the rationale. The mental attic has become quite cluttered.Peter_in_2780 wrote: As an encryption algorithm, it's nuisance value only I recognized that. Even the Tiny Encryption Algorithm (TEA)[^], which I've used a number of times, is a lot more complicated.Peter_in_2780 wrote: I have used it a few times just to discourage tampering That is definitely the purpose here.
Software Zen: delete this;
|
|
|
|