Multiple cases in switch statement with same alias - c#

i'd like to know if there's an opinion how to combine 2 cases of my switch, which are almost the same, but one is for nullable value and the second is not.
switch (rangeA)
{
case Range<int> intRangeA:
{
if (rangeB is Range<int> intRangeB)
{
return intRangeA.ValueFrom <= intRangeB.ValueTo && intRangeA.ValueTo >= intRangeB.ValueFrom;
}
return false;
}
case Range<int?> intRangeA:
{
if (rangeB is Range<int?> intRangeB)
{
return intRangeA.ValueFrom <= intRangeB.ValueTo && intRangeA.ValueTo >= intRangeB.ValueFrom;
}
return false;
}
}

It rather depends on what the data type of rangeA and rangeB are.
Assuming they're object, you can do something like this. It will throw an exception at runtime if you create a Range<Something non-comparable> then call ContainsInclusive on it. You can add an extra check for that if you want, but it gets a bit messy as Nullable<T> doesn't implement any interfaces, so you'll have to resort to reflection.
public class Program
{
public static void Main()
{
Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 });
Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 });
}
private static bool Foo(object rangeA, object rangeB)
{
return (rangeA, rangeB) switch
{
(Range<int> a, Range<int> b) => b.ContainsInclusive(a),
(Range<int?> a, Range<int?> b) => b.ContainsInclusive(a),
_ => false,
};
}
}
public class Range<T>
{
public T ValueFrom { get; set; }
public T ValueTo { get; set; }
public bool ContainsInclusive(Range<T> other)
{
return Comparer<T>.Default.Compare(other.ValueFrom, this.ValueTo) <= 0 &&
Comparer<T>.Default.Compare(other.ValueTo, this.ValueFrom) >= 0;
}
}
If you can't use the new switch expressions in this way, you can potentially do something like:
private static bool Foo(object rangeA, object rangeB)
{
return TryContainsInclusive<int>(rangeA, rangeB) ||
TryContainsInclusive<int?>(rangeA, rangeB);
}
private static bool TryContainsInclusive<T>(object a, object b)
{
if (a is Range<T> rangeA && b is Range<T> rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
return false;
}
If rangeA and rangeB can be generic types, you can get away with the simpler:
private static bool Foo<T>(Range<T> rangeA, Range<T> rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
If rangeA and rangeB can be some base Range type, then you can do something like this. Again, this will throw at runtime if T isn't comparable:
public class Program
{
public static void Main()
{
Foo(new Range<int>() { ValueFrom = 1, ValueTo = 10 }, new Range<int>() { ValueFrom = 0, ValueTo = 10 }).Dump();
Foo(new Range<int?>() { ValueFrom = 1, ValueTo = 10 }, new Range<int?>() { ValueFrom = 0, ValueTo = 10 }).Dump();
}
private static bool Foo(Range rangeA, Range rangeB)
{
return rangeB.ContainsInclusive(rangeA);
}
}
public abstract class Range
{
public abstract bool ContainsInclusive(Range other);
}
public class Range<T> : Range
{
public T ValueFrom { get; set; }
public T ValueTo { get; set; }
public override bool ContainsInclusive(Range other)
{
if (other is Range<T> o)
{
return Comparer<T>.Default.Compare(o.ValueFrom, this.ValueTo) <= 0 &&
Comparer<T>.Default.Compare(o.ValueTo, this.ValueFrom) >= 0;
}
return false;
}
}

You can also solve the problem by having a non-abstract base class Range. Both Range<int> and Range<int?> are then assignment compatible to this base class.
class Range
{
public object ValueFrom { get; protected set; }
public object ValueTo { get; protected set; }
}
class Range<T> : Range
{
public new T ValueFrom
{
get {
return (T)base.ValueFrom;
}
set {
base.ValueFrom = value;
}
}
public new T ValueTo
{
get {
return (T)base.ValueTo;
}
set {
base.ValueTo = value;
}
}
}
The properties of the generic class hide the properties of the base class. Their setter is protected. Therefore setting values is still type safe, because it can be done only through the generic class.
My solution then consists in testing the type of the From and To values. This also works well for nullable types. If a nullable is null, then nullable is int i yields false, otherwise if we have a Range<int?> it will assign nullable.Value to i.
This also allows you compare a Range<int> with a Range<int?>.
Range rangeA = new Range<int> { ValueFrom = 5, ValueTo = 12 };
Range rangeB = new Range<int?> { ValueFrom = 10, ValueTo = 18 };
if (rangeA.ValueFrom is int aFrom && rangeA.ValueTo is int aTo &&
rangeB.ValueFrom is int bFrom && rangeB.ValueTo is int bTo) {
return aFrom <= bTo && aTo >= bFrom;
}
return false;
A possible improvement is to have a strongly typed backing variable in the generic variant:
private T _valueFrom;
public new T ValueFrom
{
get {
return _valueFrom;
}
set {
base.ValueFrom = _valueFrom = value;
}
}
At least returning a value then involves no unboxing when working through the generic range.

Related

how to cast a generic type to another type?

As shown in the first class displayed, I need to cast Activité to Réunion (Réunion extends Activité) but the compiler tells me that I can't. Why? I'll put a scheme so you can better understand my classes structure and also all my other classes. Thank you.
class Employé<T>
{
private string nom;
private Local bureau;
private LinkedList<Activité<T>> activités;
public Employé(string nom, Local bureau)
{
this.nom = nom;
this.bureau = bureau;
}
public void AjouteActivité(params Activité<T>[] activités)
{
foreach(Activité<T> activité in activités)
{
if (activité as Réunion != null)
// here's the problem !!! ((Réunion)activité).EmployéConvoqués = activité;
}
}
}
Here's the scheme of my classes structure:
And here are the other classes:
abstract class Activité<T>
{
private string label;
private DateTime début, fin;
private T lieu;
private readonly int id;
private static int CPT = 0;
public Activité(string label, DateTime début, DateTime fin, T lieu)
{
this.label = label;
this.début = début;
this.fin = fin;
this.lieu = lieu;
this.id = ++CPT;
}
public override string ToString()
{
return $"{id} : {label}(de {début} à {fin}) - {DescriptionLieu()}";
}
public double Duree()
{
return fin.Subtract(début).TotalMinutes;
}
public int Id
{
//tester get; seulement
get
{
return id;
}
}
public T Lieu
{
get
{
return lieu;
}
}
public abstract string DescriptionLieu();
}
class ActivitéExtérieure : Activité<string>
{
public ActivitéExtérieure(string label, DateTime début, DateTime fin, string lieu) : base(label,début,fin,lieu) { }
public override string DescriptionLieu()
{
return Lieu;
}
}
class ActivitéInterne : Activité<Local>
{
public ActivitéInterne(string label, DateTime début, DateTime fin, Local lieu) : base(label,début,fin,lieu)
{
lieu.AjouteActivité(this);
}
public override string DescriptionLieu()
{
return $"local :: {Lieu.NumComplet}";
}
}
class Employé<T>
{
private string nom;
private Local bureau;
private LinkedList<Activité<T>> activités;
public Employé(string nom, Local bureau)
{
this.nom = nom;
this.bureau = bureau;
}
public void AjouteActivité(params Activité<T>[] activités)
{
foreach(Activité<T> activité in activités)
{
if (activité as Réunion != null)
((Réunion)activité).EmployéConvoqués = activité;
}
}
}
class Local
{
private int etage;
private int numero;
private bool possedeWifi;
private Dictionary<int, ActivitéInterne> historiquesActivités;
public int Numero
{
get
{
return numero;
}
set
{
if (value < 0 || value > 99)
throw new IndexOutOfRangeException();
else
numero = value;
}
}
public int NumComplet
{
get
{
return etage * 100 + numero;
}
}
public bool PossedeWifi
{
get
{
return possedeWifi;
}
}
public Local(int etage, bool possedeWifi, int numero)
{
this.etage = etage;
this.possedeWifi = possedeWifi;
Numero = numero;
}
public Local(int etage, int numero) : this(etage, true, numero) { }
public Local(int local, bool possedeWifi) : this(local / 100, possedeWifi, local % 100) { }
public void AjouteActivité(ActivitéInterne a)
{
historiquesActivités.Add(a.Id, a);
}
}
class Réunion : ActivitéInterne
{
private HashSet<Employé<Local>> employésConvoqués;
public Réunion(string label, DateTime début, DateTime fin, Local lieu) : base(label, début, fin, lieu) { }
public Employé<Local> EmployéConvoqués
{
set
{
employésConvoqués.Add(value);
}
}
}
The error message says that "cast is redundant". This is because you have already tested for "activité as Réunion != null". The compiler figures out that in the 'if' clause this condition is already true, therefore the cast is not meaningful. On the other hand you cannot access activité.EmployéConvoqués because the static type of activité is not Réunion.
All you have to do is introduce a new variable when testing the type. Like this:
if (activité is Réunion réunion) {
réunion.EmployéConvoqués = activité;
}
However if you try this you will see that the assignment cannot be done because you are trying to assign an activity to an Employé<Local>. These are not compatible types. Perhaps you meant something like
foreach (Activité<T> activité in activités) {
if (activité is Réunion réunion && this is Employé<Local> employéLocal) {
réunion.EmployéConvoqués = employéLocal;
}
}
Comment: in the definition of Réunion you are adding to HashSet<Employé<Local>> employésConvoqués when setting the property Employé<Local> EmployéConvoqués. From a style point of view this is strange because people generally expect a property of type Employé<Local> will represent a single Employé<Local> rather than a collection of Employé<Local>. I would suggest that you remove the setter and instead define
public void Ajoute( Employé<Local> employéConvoqué) {
this.employésConvoqués.Add(employéConvoqué);
}

How to generalize a property pattern

I have classes that has multiple properties which have well-defined name and function but have the same implementation. For example:
class Stats
{
private int attack;
public int Attack
{
get =>
HasBuff ? attack + 1 : attack;
set
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
attack = value;
}
}
public int Defense {...}
public int Speed {...}
}
Where Defense and Speed are to be implemented just like Attack . How can I generalize this structure to avoid redundancy and make changes easier?
Make another class to generalize stats:
public class Stat
{
public bool HasBuff { get; set; }
private int _stat;
public int Score
{
get => HasBuff ? _stat + 1 : _stat;
set => _stat = value;
}
}
Then just use that for each of your skills:
public class CombatStats
{
public Stat Attack { get; } = new Stat();
public Stat Defense { get; } = new Stat();
public Stat Speed { get; } = new Stat();
}
Calling code would look like this:
var ninja = new Ninja();
ninja.skills = new CombatStats();
var attackStrength = ninja.skills.Attack.Score;
As further improvement, implicit operators can be used to avoid object creation and call to Score:
public class Stat
{
...
public static implicit operator int(Stat stat)
{
return stat.Score;
}
public static implicit operator Stat(int value)
{
return new Stat()
{
Score = value
};
}
}
This makes the change transparent to client code written w.r.t. to the example in the question:
ninja.skills = new CombatStats(){
Attack = 5,
Defense = 2
}
int attack = ninja.skills.Attack;
One approach to consider:
class Stats
{
// other existing code here
private int defense;
public int Defense
{
get
{
return GetValue(defense);
}
set
{
SetValue(value, ref defense);
}
}
private int GetValue(int value)
{
return HasBuff ? value + 1 : value;
}
private void SetValue(int value, ref int target)
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
target = value;
}
}
Attack etc will now be basically the same as Defence but passing in attack rather than defense to GetValue and SetValue.
I would go with composition
Stat:
public class Stats
{
private readonly StatProperty _defense;
private readonly StatProperty _attack;
private readonly StatProperty _speed;
public Stats()
{
_defense = new StatProperty(this);
_attack = new StatProperty(this);
_speed = new StatProperty(this);
}
public int Defense
{
get => _defense.Value;
set => _defense.Value = value;
}
public int Attack
{
get => _attack.Value;
set => _attack.Value = value;
}
public int Speed
{
get => _speed.Value;
set => _speed.Value = value;
}
public bool HasBuff { get; set; }
}
StatProperty:
public class StatProperty
{
public Stats Stats { get; }
public StatProperty(Stats stats)
{
Stats = stats;
}
private int _value = 1;
public int Value
{
get => Stats.HasBuff ? _value + 1 : _value;
set
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
_value = value;
}
}
}
I would need more details to know if it is the best option.
you also could make StatProperty as internal if don't want to show it outside of your library or nested private class if you want to use this just on the class Stats

instance access to class members of inherited abstract

This is theory Thursday I guess.
Shouldn't Main() have access to _XLocal & _YLocal?
using System;
namespace HelloGoodbyeOperator {
public abstract class HGOperator {
public string _greeting { get; set; }
public bool _x { get; internal set; }
public bool _y { get; internal set; }
public static implicit operator HGOperator(bool mode) {
object ret = new object();
if (mode)
ret = new HGOperator_Hello { _greeting = "hello", _XLocal = 10 };
else
ret = new HGOperator_Goodbye { _greeting = "goodbye", _YLocal = 20 };
return (HGOperator)ret;
}
}
public class HGOperator_Hello : HGOperator {
public int _XLocal { get; set; }
public HGOperator_Hello() { _x = true; Console.WriteLine("HGOperator_Hello //" + _XLocal.ToString() + "\\\\"); }
}
public class HGOperator_Goodbye : HGOperator {
public int _YLocal { get; set; }
public HGOperator_Goodbye() { _y = false; Console.WriteLine("HGOperator_Goodbye //", _YLocal, "\\\\"); }
}
class Program {
static void Main(string[] args) {
HGOperator hg = true;
Console.WriteLine(hg._greeting);
test(hg);
Console.WriteLine("");
hg = false;
Console.WriteLine(hg._greeting);
test(hg);
Console.ReadKey();
}
static void test(HGOperator hg) {
if (hg is HGOperator_Hello) {
Console.WriteLine(hg._x);
//Console.WriteLine(hg._XLocal);
} else {
Console.WriteLine(hg._y);
//Console.WriteLine(hg._YLocal);
}
}
}
}
Here is the output
HGOperator_Hello //0\
hello
True
HGOperator_Goodbye //
goodbye
False
I can understand how trying to access hg._YLocal of a HGOperator_Hello type would be a nightmare & vise-versa. But would still think I could get to the respective members with caution.
Also and I will bet this is realted. The two concrete constructors do not have a value for _XLocal & _YLocal on the Console.Writeline()s. Without the .ToString() just a "" is printed. Why not?
Thanks.
The issue is that the compiler doesn't know that hg is a derived type of HGOperator_Hello or HGOperator_Goodbye. So inside your if you need to create another variable and cast it:
if (hg is HGOperator_Hello)
{
var helloHg = (HGOperator_Hello)hg;
Console.WriteLine(helloHg._x);
Console.WriteLine(helloHg._XLocal);
}
else
{
var goodbyeHg = (HGOperator_Goodbye)hg;
Console.WriteLine(goodbyeHg._y);
Console.WriteLine(goodbyeHg._YLocal);
}

set multiple return value for method declaration

I have a function as below:
public var UpdateMapFetcher(int stationID, int typeID)
I need this function to return either string or int.
My return value is set as below
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return returnvalue;
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return a;
}
How to have either one data type as return value in dynamic environment.
My intuition tells me, that you are trying to convert string value to some type. In that case you can use:
public T UpdateMapFetcher<T>(int stationID)
{
//var someValue = "23";
return (T)Convert.ChangeType(someValue, typeof(T));
}
//then
var typed = UpdateMapFetcher<int>(6);
In case you don't know T, you can use mapping (0-int, 1-string, etc.):
public object UpdateMapFetcher(int stationID, int type)
{
var typeMap = new []{ typeof(int), typeof(string)};
//var someValue = "23";
return Convert.ChangeType(someValue, typeMap[type]);
}
//then
var untyped = UpdateMapFetcher(6, 0/*0 is int*/);
if (untyped.GetType() == typeof(int))
{ /*is int*/
}
Another solution is to use implicit conversions:
public class StringOrInt
{
private object value;
public ValueType Type { get; set; }
public static implicit operator StringOrInt(string value)
{
return new StringOrInt()
{
value = value,
Type = ValueType.String
};
}
public static implicit operator StringOrInt(int value)
{
return new StringOrInt()
{
value = value,
Type = ValueType.Int
};
}
public static implicit operator int(StringOrInt obj)
{
return (int)obj.value;
}
public static implicit operator string(StringOrInt obj)
{
return (string)obj.value;
}
}
public enum ValueType
{
String,
Int
}
And then (simplified):
public static StringOrInt UpdateMapFetcher(int stationID, int typeID)
{
if (typeID == 0)
return "Text";
return 23;
}
private static void Main(string[] args)
{
var result = UpdateMapFetcher(1, 1);
if (result.Type == ValueType.String) { }//can check before
int integer = result;//compiles, valid
string text = result;//compiles, fail at runtime, invalid cast
}
you can return an object. You'd have to subsequently check for types in your consuming method. I assume that won't be a problem in your usecase.
your method signature is therefore:
public object UpdateMapFetcher(int stationID, int typeID)
You also have the option of using the out keyword, which permits you to accept both into variables and check after the function has been called.
public void UpdateMapFetcher(int stationID, int typeID, out int intValue, out string strValue)
// or int return val and out string value
public int UpdateMapFetcher(int stationID, int typeID, out string strValue)
With the use appearing something like this:
int intVal;
string strVal;
UpdateMapFetcher(stationID, typeID, out intVal, out strVal);
if (strVal != null)
{
doSomethingWithString(strVal);
}
else
{
doSomethingWithInt(intVal);
}
Frankly, I would just return a Tuple, with string being non-null indicating string value to use, and null as indicator for int return
public Tuple<string, int> UpdateMapFetcher(int stationID, int typeID) {
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return new Tuple<string, int>(returnvalue, 0);
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return new Tuple<string, int>(null, a);
}
}
On consumer side
var rc = UpdateMapFetcher( .... );
if (rc.Item1 != null) {
// code to use string value
} else {
// code to use int value
}
I would choose to return an object of new class which might look like this:
class Result {
public string StringValue { get; }
public string Int32Value { get; }
public bool IsString { get; }
public bool IsInt32 { get; }
public Result(string value) {
StringValue = value;
IsString = true;
}
public Result(int value) {
Int32Value = value;
IsInt32 = true;
}
}
This way you can check which Type is it by using Isxxx property. You can also enhance this with validation in value geters. F. e., for string it might look like this:
public string StringValue {
get {
if (IsString)
return m_stringValue;
throw new InvalidOperationException("Value is not a string.");
}
}
You can't really do exactly that, but there are several ways to do more or less what you want. You'd probably be better off change the design a little though.
Two ideas:
Either change your code to use two different methods, and call each of them as needed instead.
..Or return an object, which you can cast however you like..
..Or, use a generic method with TypeDescriptor, like the following.
Note that we here convert the value to string first even if it was an int, since we can then use a common method ConvertFromString() to convert it to whatever type T was.
public T UpdateMapFetcher<T>(int stationID, int typeID) {
// To allow parsing to the generic type T:
var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
if(converter != null)
{
return (T)converter.ConvertFromString(returnvalue.ToString());
}
else
{
return default(T);
}
}
Usage:
var result = MyExtensions.UpdateMapFetcher<string>(1, 2);
or:
var result = MyExtensions.UpdateMapFetcher<int>(1, 2);
You can return Object and cast to type which you want.
public Object UpdateMapFetcher(int stationID, int typeID)
if (finaloutput == "System.String")
{
// param1[i] = Convert.ChangeType(typeID_New.ToString(), typeof(string));
returnvalue = returnvalue.ToString();
return returnvalue;
}
else if (finaloutput == "System.Int32")
{
int a=0;
a = Convert.ToInt32(returnvalue);
return a;
}
A type that can contain either one type or another is usually called (unsurprisingly) Either. It is a special case of a sum type, basically a discriminated union, tagged union, or disjoint union with exactly two cases (instead of an arbitrary number).
Unfortunately, there does not exist an implementation of an Either type in the standard libraries, but there are plenty of implementations to be found on Google, GitHub, and elsewhere … and porting one of the existing implementations from e.g. Haskell or Scala isn't that hard, either.
It looks a bit like this (forgive my code, I don't actually know C♯ that well):
using System;
abstract class Either<A, B>
{
public abstract bool IsLeft { get; }
public abstract bool IsRight { get; }
public abstract A Left { get; }
public abstract B Right { get; }
public abstract A LeftOrDefault { get; }
public abstract B RightOrDefault { get; }
public abstract void ForEach(Action<A> action);
public abstract void ForEach(Action<B> action);
public abstract void ForEach(Action<A> leftAction, Action<B> rightAction);
private sealed class L : Either<A, B>
{
private A Value { get; }
public override bool IsLeft => true;
public override bool IsRight => false;
public override A Left => Value;
public override B Right { get { throw new InvalidOperationException(); } }
public override A LeftOrDefault => Value;
public override B RightOrDefault => default(B);
public override void ForEach(Action<A> action) => action(Value);
public override void ForEach(Action<B> action) {}
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => leftAction(Value);
internal L(A value) { Value = value; }
}
private sealed class R : Either<A, B>
{
private B Value { get; }
public override bool IsLeft => false;
public override bool IsRight => true;
public override A Left { get { throw new InvalidOperationException(); } }
public override B Right => Value;
public override A LeftOrDefault => default(A);
public override B RightOrDefault => Value;
public override void ForEach(Action<A> action) {}
public override void ForEach(Action<B> action) => action(Value);
public override void ForEach(Action<A> leftAction, Action<B> rightAction) => rightAction(Value);
internal R(B value) { Value = value; }
}
public static Either<A, B> MakeLeft(A value) => new L(value);
public static Either<A, B> MakeRight(B value) => new R(value);
}
And you'd use it like this:
static class Program
{
public static void Main()
{
var input = Console.ReadLine();
int intResult;
var result = int.TryParse(input, out intResult) ? Either<int, string>.MakeLeft(intResult) : Either<int, string>.MakeRight(input);
result.ForEach(r => Console.WriteLine("You passed me the integer one less than " + ++r), r => Console.WriteLine(r));
}
}

How to validate classes in a hierarchy in a generic type-safe way?

I'm stuck with what seemed to be a very simple task at the very beginning. I have a class hierarchy each class in which can define its own validation rules. Defining validation rules should be as simple as possible. Here is what is almost what is needed:
class HierarchyBase
{
private List<Func<object, bool>> rules = new List<Func<object, bool>>();
public int fieldA = 0;
public HierarchyBase()
{
AddRule(x => ((HierarchyBase)x).fieldA % 2 == 0);
}
protected virtual void Operation()
{
fieldA++;
}
protected void AddRule(Func<object, bool> validCriterion)
{
rules.Add(validCriterion);
}
public void PerformOperation()
{
Operation();
Validate();
}
protected virtual void Operation()
{
fieldA++;
}
private void Validate()
{
IsValid = rules.All(x => x(this));
}
public bool IsValid
{
get;
private set;
}
}
There is one more thing that is needed - type safety when adding validation rules. Otherwise each sub class will have to do those casts that just look awkward. Ideally Func<T, bool> would work, but there is a whole bunch of issues with that: we cannot inherit our HierarchyBase from any kind of IValidatable<HierarchyBase> as the inheritance hierarchy can be N levels deep (yeah, I feel the smell as well); storing any concrete Func<HierarchyBaseInheritor, bool> in rules and traversing them.
How would you introduce type-safety here?
The right approach is to make each class in the hierarchy responsible for validating itself:
HierarchyBase:
class HierarchyBase
{
public int A { get; set; }
public bool Validate()
{
return this.OnValidate();
}
protected virtual bool OnValidate()
{
return (this.A % 2 == 0);
}
}
HierarchyBaseInheritorA:
class HierarchyBaseInheritorA : HierarchyBase
{
public int B { get; set; }
protected override bool OnValidate()
{
return base.OnValidate() &&
(this.A > 10) &&
(this.B != 0);
}
}
HierarchyBaseInheritorB:
class HierarchyBaseInheritorB : HierarchyBaseInheritorA
{
public int C { get; set; }
protected override bool OnValidate()
{
return base.OnValidate() &&
(this.A < 20) &&
(this.B > 0) &&
(this.C == 0);
}
}
Usage:
var result = new HierarchyBaseInheritorB();
result.A = 12;
result.B = 42;
result.C = 0;
bool valid = result.Validate(); // == true
Note: The following solution is an in-joke between me and Eric Lippert. It works, but is probably not to be recommended.
The idea is to define a generic type parameter that refers to the "current" type (like this refers to the "current" object).
HierarchyBase:
class HierarchyBase<T>
where T : HierarchyBase<T>
{
protected readonly List<Func<T, bool>> validators;
public HierarchyBase()
{
validators = new List<Func<T, bool>>();
validators.Add(x => x.A % 2 == 0);
}
public int A { get; set; }
public bool Validate()
{
return validators.All(validator => validator((T)this));
}
}
HierarchyBaseInheritorA:
class HierarchyBaseInheritorA<T> : HierarchyBase<T>
where T : HierarchyBaseInheritorA<T>
{
public HierarchyBaseInheritorA()
{
validators.Add(x => x.A > 10);
validators.Add(x => x.B != 0);
}
public int B { get; set; }
}
HierarchyBaseInheritorB:
class HierarchyBaseInheritorB : HierarchyBaseInheritorA<HierarchyBaseInheritorB>
{
public HierarchyBaseInheritorB()
{
validators.Add(x => x.A < 20);
validators.Add(x => x.B > 0);
validators.Add(x => x.C == 0);
}
public int C { get; set; }
}
Usage:
var result = new HierarchyBaseInheritorB();
result.A = 12;
result.B = 42;
result.C = 0;
bool valid = result.Validate(); // == true

Categories

Resources