GF schreef op Thursday 05 January 2006 @ 18:33:
Ook nuttig zo'n stuk code. Enige wat je hier uit kan afleiden is dat er een exceptie kan optreden. Welke? Vang alle mogelijke excepties apart op en maak geen gebruik van algemene exception classes. Dat vind ik toch zo ranzig
tja, maar er in een catch niets mee doen is onzin. Je kunt dan net zo goed de catch weglaten.
Voorbeeldje zoals het wel moet:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
| try
{
string blaat = "0030";
int example = Convert.ToInt32(blaat);
}
catch(FormatException)
{
throw;
}
catch(OverflowException)
{
throw;
} |
Dit is toch onzinnige code? waarom die catches?
of
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
| try
{
string blaat = "0030";
int example = Convert.ToInt32(blaat);
}
catch(FormatException ex)
{
// do something with the exception like a dialog or something
}
catch(OverflowException ex)
{
// do something with the exception like a dialog or something
} |
In een method zou ik dit nog wel kunnen begrijpen, omdat je bv input data aan het processen bent en een default waarde van 0 hebt bv voor nietaanwezige input of fout geformuleerde input. Bij foute input kun je dus volstaan met een lege catch en die in een method stoppen zodat je je data processing code niet vervuild EN je verder kunt, want bij een exception kun je geen resume doen:
int customerID = SaveStringToInt(customerIDAsString, 0);
en:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
| public int SaveStringToInt(string value, int defaultValue)
{
int toReturn = defaultValue;
try
{
toReturn = Convert.ToInt32(value);
}
catch
{
}
return toReturn;
} |
In request.querystring processing code kom je dit nog wel eens tegen en helemaal niet verkeerd want het is de meest simpele en solide methode om dit te doen.
Nog een voorbeeld
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
| foreach (Item item in items)
{
if(condition1 && condition2 && condition3)
{
if(condition4 && condition5 && condition6)
{
if(condition7 && condition8 && condition9)
{
// do your thing
}
}
}
} |
Doe dit aub zo (oid.). Lees een stuk prettiger (ook voor jezelf)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| foreach (Item item in items)
{
if(!condition1 || !condition2 || !condition3)
{
continue;
}
if(!condition4 || !condition5 || !condition6)
{
continue;
}
if(!condition7 || !condition8 || !condition9)
{
continue;
}
// do your thing
} |
Nee dat is een mindere oplossing. want de jouwe is veel minder leesbaar, want je code is niet opgezet als "ik doe 'do your thing' wanneer de volgende conditie waar is", en dus veel minder onderhoudbaar.
Gebruik continue alleen wanneer je in dit soort situaties zit:
for(...)
{
hier testen of je die 20 regels code inmoet, zo nee: continue
/*
20 regels code
*/
}
Jij gebruikt een not test in je if statement. Deze zijn moeilijker te onderhouden en niet alleen om de leesbaarheid. Een not test in een if statement is normaliter alleen logisch wanneer de not test de enige is om de waar conditie te formuleren om iets te doen.
Jouw code komt nl. neer op:
if(expr)
{
doe niets
}
else
{
doe wat je eigenlijk wilt
}
dit is brakke code want je weet nl. niet wat expr deed falen. Als dat iets is waar je in else mee uit de voeten moet ben je het haasje. Beter is te testen op wat WAAR moet zijn om wat je wilt doen ook te doen. En dat doen die 3 geneste if statements perfect. Ik zou er 1 expressie van maken, maar dat terzijde, ik kan begrijpen waarom er voor 3 geneste ifs gekozen is, om het leesbaar te houden. Ik bedoel, ik heb ook zo'n test:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| /// <summary>
/// Determines if the field should be set to the value passed in.
/// </summary>
/// <param name="fieldToSet">Field to set.</param>
/// <param name="entityIsNew"><see langword="true"/> if [entity is new]; otherwise, <see langword="false"/>.</param>
/// <param name="value">Value.</param>
/// <returns>true if the field should be set with the value, false otherwise</returns>
internal static bool DetermineIfFieldShouldBeSet(IEntityFieldCore fieldToSet, bool entityIsNew, object value)
{
// field value has to be updated in the following situations:
// - when the entity is new and:
// - the field hasn't been changed
// - the field has been changed but the value is different, only if the current value is not null
// - when the entity is not new and:
// - the field is already changed
// - the field's DbValue is null and value is not null
// - the field's DbValue is not null and the field's CurrentValue is different than the new value and not null
// - the field's CurrentValue is null
return(
(
entityIsNew &&
(
!fieldToSet.IsChanged
||
(
fieldToSet.IsChanged
&&
(
(
(fieldToSet.CurrentValue!=null)
&&
!ValuesAreEqual(fieldToSet.CurrentValue, value)
)
||
(fieldToSet.CurrentValue==null)
)
)
)
)
||
(
!entityIsNew
&&
(
fieldToSet.IsChanged
||
(
(fieldToSet.DbValue==null)
&&
(value != null)
)
||
(fieldToSet.CurrentValue==null)
||
(
(fieldToSet.DbValue!=null)
&&
(fieldToSet.CurrentValue!=null)
&&
!ValuesAreEqual(fieldToSet.CurrentValue, value)
)
)
)
);
} |
Die heb ik zo opgeschreven zodat het leesbaarder is, het is tenslotte geen code voor een obfuscation wedstrijd.
Om terug te komen op continue: continue is bedoeld om een loop opnieuw te starten wanneer je constateert dat de toestand waarin de locale data van de loop zich in moet bevinden niet de juiste is. Dit lijkt met jouw oplossing samen te vallen maar schijn bedriegt: continue in jouw situatie simplificeert de code niet. Je kunt bv met een enkele test kijken of je verder moet en of je daarmee grote if-else bomen kunt vermijdden verderop. Indien dat niet het geval is, vermijdt continue en doorloop gewoon de loop.
voorbeeld:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| foreach(ActionQueueElement element in queueToPersist)
{
bool saveSucceeded = true;
IEntity2 entityToSave = (IEntity2)element.Entity;
// do test if entity is really dirty. An entity can end up in the queue and not be marked dirty if the entity
// had fk syncs pending (for example an entity which is an intermediate entity for an m:n relation, with solely fk fields)
// and the PK syncs turned out to be not succesful, because the fk fields already contained that value.
// if the entity turns out to be not dirty and not new, simply skip it and move on to the next one.
if(!entityToSave.IsDirty && !entityToSave.IsNew)
{
// not changed nor new, no fields to update, skip
continue;
}
IPredicateExpression updateRestriction = element.AdditionalUpdateFilter;
TraceHelper.WriteIf(TraceHelper.PersistenceExecutionSwitch.TraceVerbose, ((EntityBase2)entityToSave).GetEntityDescription(TraceHelper.PersistenceExecutionSwitch.TraceVerbose, entityToSave), "Current persisted entity info");
if(_isTransactionInProgress)
{
// add ourselves physically to the transaction so values will be rolled back when the transaction rolls back.
AddTransactionParticipant(entityToSave);
}
///.50 or more lines of code here... |
Hier is de code van de loop niet simpeler wanneer ik daar neerzet:
if((entityToSave.IsDirty || entityToSave.IsNew)
{
// doe 75 regels code
}
ipv dat ik vooraf test of ik daar moet zijn, nee dus ga ik verder met de volgende in de rij (dit is een queue persister loop).
Wellicht onduidelijk voorbeeld, maar jouw 3 if statements met 3 continues vind ik niet beter, temeer omdat het meerdere tests zijn en de echte reden waarom de code moet worden uitgevoerd eigenlijk alleen maar onduidelijker maken. Jij moet nu echt gaan nadenken wat de conditie is wanneer 'do your thing' wordt uitgevoerd.