Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This is a common design challenge in an inheritance hierarchy - how to perform class-dependent behavior in the constructor. The reason code analysis tools flag this as a problem is that the constructor of the derived class has not yet had an opportunity to run at this point, and the call to the virtual method may depend on state that has not been initialized.</p> <p>So you have a few choices here:</p> <ol> <li><strong>Ignore the problem.</strong> If you believe that implementers should be able to write a parameter validation method without relying on any runtime state of the class, then document that assumption and stick with your design.</li> <li><strong>Move validation logic into each derived class constructor,</strong> have the base class perform just the most basic, abstract kinds of validations it must (null checks, etc).</li> <li><strong>Duplicate the logic in each derived class.</strong> This kind of code duplication seems unsettling, and it opens the door for derived classes to <em>forget</em> to perform the necessary setup or validation logic.</li> <li><strong>Provide an Initialize() method of some kind</strong> that has to be called by the consumer (or factory for your type) that will ensure that this validation is performed after the type is fully constructed. This may not be desirable, since it requires that anyone who instantiates your class must remember to call the initialization method - which you would think a constructor could perform. Often, a Factory can help avoid this problem - it would be the only one allowed to instantiate your class, and would call the initialization logic before returning the type to the consumer.</li> <li><strong>If validation does not depend on state,</strong> then factor the validator into a separate type, which you could even make part of the generic class signature. You could then instantiate the validator in the constructor, pass the parameters to it. Each derived class could define a nested class with a default constructor, and place all parameter validation logic there. A code example of this pattern is provided below.</li> </ol> <p>When possible, have each constructor perform the validation. But this isn't always desirable. In that case, I personally, prefer the factory pattern because it keeps the implementation straight forward, and it also provides an interception point where other behavior can be added later (logging, caching, etc). However, sometimes factories don't make sense, and in that case I would seriously consider the fourth option of creating a stand-along validator type.</p> <p>Here's the code example:</p> <pre><code>public interface IParamValidator&lt;TParams&gt; where TParams : IActionParameters { bool ValidateParameters( TParams parameters ); } public abstract class BaseBusinessAction&lt;TActionParameters,TParamValidator&gt; where TActionParameters : IActionParameters where TParamValidator : IParamValidator&lt;TActionParameters&gt;, new() { protected BaseBusinessAction(TActionParameters actionParameters) { if (actionParameters == null) throw new ArgumentNullException("actionParameters"); // delegate detailed validation to the supplied IParamValidator var paramValidator = new TParamValidator(); // you may want to implement the throw inside the Validator // so additional detail can be added... if( !paramValidator.ValidateParameters( actionParameters ) ) throw new ArgumentException("Valid parameters must be supplied", "actionParameters"); this.Parameters = actionParameters; } } public class MyAction : BaseBusinessAction&lt;MyActionParams,MyActionValidator&gt; { // nested validator class private class MyActionValidator : IParamValidator&lt;MyActionParams&gt; { public MyActionValidator() {} // default constructor // implement appropriate validation logic public bool ValidateParameters( MyActionParams params ) { return true; /*...*/ } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload