(How)
C^# You Are - 15 June 2008
These questions revolve around some of the code analysis errors you might run
across when developing code with code analysis enabled. Note that the code
used in these questions are for demonstrating the problem and should not be
considered good, proper coding styles.
- This code generates the error Collection properties should be read only. What is wrong with this? Answer
public class Department
{
public EmployeeCollection Employees
{
get { return m_Employees; }
set { m_Employees = value; }
}
private EmployeeCollection m_Employees = new EmployeeCollection();
}
CA2227: The problem is that you are gaining very little over having a read-only
property. Items in the collection can still be modified, added or removed.
Making a collection property writable allows someone to replace your existing
collection with a brand new one and this is rarely a good idea. It also
limits maintainability since, at a future date, you might chose to use a
specialized, or even new, collection type. Making the collection property
writable would prevent this.
This rule should be excluded only in extremely rare circumstances. In all
other cases redesign your code to eliminate it.
- This code generates the error Do not cast unnecessarily Why? Answer
public class CueBanne public void SetCueBanner ( Control control, string text )
{
if (control is ComboBox)
{
ComboBox cb = (ComboBox)control;
...
} else if (control is TextBox)
{
TextBox tb = (TextBox)control;
...
};
}
}
CA1800:
The is operator is useful but only in limited circumstances. In
general you should use the as operator instead. The problem is a
performance issue. If you use the is operator and then use either
an as operator or an explicit cast to convert to the type you just
checked then you will be paying for the runtime typechecking twice.
Whenever you do an explicit cast or use the as operator the CLR must
verify the type is correct. The only difference between the two is that
one will throw an exception and one won't. Since it is rare that you ever
just want to check the type of an object you should rarely use is.
This rule should never be excluded. The size of the written code has no
relation to the runtime size or performance so use the right operator instead.
- This code generates the error Enums should have zero value. Why? Answer
[Flags]
public enum OperationOptions
{
IncludeEmpty = 0x1,
ExcludeLarge = 0x2,
AddPrefix = 0x3,
}
CA1008: Enumerations
are glorified integral values, even in .NET. The default value of an
enumeration is zero. If you do not define an enum value for zero then the
enumeration will still get a zero value but it will not map to anything.
Contrary to popular belief it is perfectly legal to assign an integral value to
an enumeration variable. The CLR will not complain. However if the
numeric value does not map to a valid combination then displaying the value will
just display the number.
In the MSDN documentation you might have noticed that any member that accepts an
enumeration as a parameter often will specify an exception that occurs when the
parameter is not a valid valid. It is a safety measure. CA1008 is
designed to help you find such issues.
The only time you might want to remotely consider excluding this rule is if the
enumeration is used for mapping values to unmanaged code. Even then it
might not hurt.
- This code generates the error Mark members as static. Why? Answer
public class
SimpleClass
{
protected string FormatObject ( object value, string format )
{
return value.ToString(format);
}
}
CA1822: This error is
raised when you use a non-static, non-virtual method inside a class and the
method does not reference any non-static members of the class. CA1822 is a
performance rule. It is pointing out that instance methods have more
overhead than static methods. Since the method in question does not have
any instance data it would perform better if you were to make it a static
member.
This rule can be excluded when it does not make sense to mark the member as
static. A protected member might be a good example.
- This code generates the error Interface methods should be callable by child
types. Why? Answer
public class
EmployeeCollection : ICollection<T>
{
public Employee[] ToArray ( ) { ... }
void ICollection<T>.CopyTo ( T[] array, int startingIndex )
{ ... }
}
CA1033: This error is
generated when you have a non-sealed class that implements an interface member
in a non-virtual member. In this case the error is telling you that, since
the class is not sealed, derived classes will not be able to override or change
the underlying interface implementation because the base class does not expose a
virtual method.
This rule can be ignored if this is the behavior you want. Sometimes you do
not want a derived class to override an interface implementation. You
should examine each occurrence of this error and determine if a derived class
might ever need to override the base implementation. If so then you should
make the member virtual or call a virtual method. Otherwise you can safely
exclude the error.
- This code generates the error Specify StringComparison. Why? Answer
public
static string Trim ( string trimString, string value )
{
int len = trimString.Length;
while (value.StartsWith(trimString))
value = value.Substring(len);
while (value.EndsWith(trimString))
value = value.Substring(0, value.Length - len - 1);
return value;
}
CA1307: This
rule is for globalization. Whenever you are dealing with comparing strings
or formatting certain types like numbers, dates or currency then globalization
becomes a concern. In any of these cases you should consider how to handle
non-English languages (or whatever your default language is). For string
comparisons you should expose an overload that accepts a StringComparison
parameter. This allows users to handle the comparison based upon the
current language or in a language neutral manner. Furthermore case sensitivity
now becomes an issue for the caller to solve. Note that you can (should)
still expose overloads that do not require any additional parameters.
These overloads can use a standard set of options instead.
This rule should never be excluded. Even if you are not planning for world
domination today you might be in the future. Additionally case sensitivity
is always an issue so it is better to pass the buck onto the caller rather than
having to deal with it yourself.
-
This code generates the error Do not ignore method results. Why? Answer
public
object GetKeyValue ( string key )
{
object value;
dictionary.TryGetValue(key, out value);
return value;
}
CA1806: The error is
in regards to ignoring the return value of a method. This practice is one
of the reasons why .NET moved away from error codes for functions.
In the code above the return value indicates whether the value was successfully
retrieved. In this particular case the returned value will always be set
by the called method irrelevant of whether it was successful or not.
Therefore in this particular case it might be OK to ignore the return value.
It depends upon what the method should do when the key is missing. If the
method is going to throw an exception if the key cannot be found then it
probably is not necessary to call the TryGetValue. It would make
more sense to simply index the dictionary and let the dictionary throw the
exception.
Each occurrence of this rule should be evaluated. In most cases the method
return value should be used. In the rare case where it is meaningless then
you can exclude this rule.
- This code generates the error Instantiate argument exceptions correctly.
Why? Answer
public
void InitializeObject ( string value )
{
if (value == null)
throw new ArgumentNullException("value", "Value is null.");
if (value.Length < 10)
throw new ArgumentException("value", "Value must be at least 10
characters.");
...
}
CA2208: This is
a good one. Evidently the developer who wrote ArgumentException did
not talk to the developer who wrote ArgumentNullException (and some
others). In the former case the parameter ordering is message, name.
In the latter it is name, message. They are backwards.
Fortunately we have Intellisense (usually) to help us out.
This error gets tripped when it detects your usage of one of these exceptions and
the name parameter does not match the name of one of the parameters to the
method in question. This indicates a problem.
Except for one circumstance this error should never be excluded. The
exception is if you use helper methods to build exception objects. In this
case the parameter names are probably passed to the helper method rather than
being the helper method names. Code analysis is not smart enough to
realize what you are trying to do. You can therefore exclude the error in this case.
- This code generates the error Do not expose generic lists. Why? Answer
public class
Department
{
...
public List<Employee> Employees
{
get { return m_Employees; }
}
}
CA1002: This
rule gets violated all the time. To be fair it is rarely the fault of the
developer but rather the confusion that .NET has caused. Lists and
collections are different beasts even though they do similar things.
Making this even worse is the fact that the interfaces are what truly
differentiates them but the standard implementation classes implement both the
interfaces. This causes major confusion.
You should almost never expose List/List<T> directly in code. These
classes are designed to be used as implementation details and everybody knows
not to expose implementation details. Collections are more flexible (from
a caller's standpoint) and should be used instead.
Each occurrence of this rule should be evaluated. In most cases the error
should be fixed. The only exception is if you are implementing support
classes for use inside a library (in which case they should probably be
internal) or if you are implementing collection support classes. Even in
this particular case you should evaluate whether a collection would work better.
- This code generates the error Override equals and operator equals on value
types. Why? Answer
public struct Address
{
public string Street { get { get } }
public string City { get { ... } }
public string State { get { ... } }
public string ZipCode { get { ... } }
private string m_Street, m_City, m_State, m_Zip;
}
CA1815: Value types
have a default equality operator defined for them automatically. This is
great. The not so great part is that it uses reflection to compare the
field values. It is always faster to explicitly define the equality/not
equal operators and do it yourself.
This error should rarely be excluded. The only viable time it could be is for
nested types or in interop structures where equality is never used.
|
|