C# Как правильно реализовать модель?
ВВЕДЕНИЕ
У меня есть класс Module. Он состоит, по-сути, только из одного поля - Symbol. Также он имеет перегруженные методы хеша и сравнения, где равными считаются модули с одинаковым Symbol.
public class Module : IEquatable<Module>
{
public readonly char Symbol;
public Module(char symbol)
{
Symbol = symbol;
}
/// <returns><see langword="true"/> if <paramref name="obj"/> is <see cref="Module"/> and modules are equal</returns>
public override bool Equals(object obj) => Equals(obj as Module);
/// <returns><see langword="true"/> if modules symbols are equal</returns>
public bool Equals(Module module)
{
if (module is null)
return false;
if (ReferenceEquals(this, module))
return true;
if (GetType() != module.GetType())
return false;
return module.Symbol == Symbol;
}
public static bool operator ==(Module module1, Module module2)
{
if (module1 is null)
{
if (module2 is null)
return true;
else
return false;
}
return module1.Equals(module2);
}
public static bool operator !=(Module module1, Module module2) => !(module1 == module2);
public override int GetHashCode() => Symbol.GetHashCode();
}
Есть несколько наследников этого класса. Один из них - ParametricModule<T>. Он содержит в себе дополнительно параметр, и равенство двух таких модулей определяется, логично, по равенству Symbol и Parameter:
public class ParametricModule<T> : Module, ICloneable, IEquatable<ParametricModule<T>>
{
public readonly T Parameter;
public ParametricModule(char symbol, T parameter) : base(symbol)
{
Parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
}
/// <returns><see langword="true"/> if <paramref name="obj"/> is <see cref="Module"/> and modules are equal</returns>
public override bool Equals(object obj) => Equals(obj as ParametricModule<T>);
/// <returns><see langword="true"/> if modules symbols and parameters are equal</returns>
public bool Equals(ParametricModule<T> module)
{
if (module is null)
return false;
if (ReferenceEquals(this, module))
return true;
return module.Parameter.Equals(Parameter) && base.Equals(module);
}
public static bool operator ==(ParametricModule<T> module1, ParametricModule<T> module2)
{
if (module1 is null)
{
if (module2 is null)
return true;
else
return false;
}
return module1.Equals(module2);
}
public static bool operator !=(ParametricModule<T> module1, ParametricModule<T> module2) => !(module1 == module2);
public override int GetHashCode() => (Symbol, Parameter).GetHashCode();
}
ПРОБЛЕМА
Проблема состоит в том, что мне необходимо создать и передавать некоторый шаблон модуля. Шаблоны должны равнятся, если типы классов и Symbol-ы модулей совподают. Я хочу получить такой функционал:
var moduleA = new Module('A');
var moduleA0 = new ParametricModule<int>('A', 0);
var moduleA1 = new ParametricModule<int>('A', 1);
moduleA != moduleA0 && moduleA != moduleA1 // РЕАЛИЗОВАНО: разные типы НЕ РАВНЫ
moduleA.Template != moduleA0.Template && moduleA.Template != moduleA1.Template // ХОЧЕТСЯ: шаблоны разных типов НЕ РАВНЫ
moduleA0 != moduleA1 // РЕАЛИЗОВАНО: одинаковые типы с одинаковой буквой, но с разными параметрами НЕ РАВНЫ
moduleA0.Template == moduleA1.Template // ХОЧЕТСЯ: шаблоны одинаковых типы с одинаковой буквой РАВНЫ
ВОПРОС
Как лучше всего реализовать подобный шаблон, его хранение и получение?
Моё виденье шаблона модуля очень похоже на то же, что если бы все наследники класса Module можно было привести к Module и использовать его родную функцию сравнения и получения хеша.
Ответы (1 шт):
Создаём класс шаблона (GetHashCode нужно дописать):
public class Template : IEquatable<Template>
{
private readonly Type Type;
private readonly Module Module;
public Template(Type type, Module module)
{
Type = type;
Module = module;
}
public bool Equals(Template other)
{
if (this.Type != other.Type)
return false;
return this.Module.Equals(other.Module);
}
public override bool Equals(object obj)
{
if (obj is not Template other)
return false;
return this.Equals(other);
}
public static bool operator ==(Template template1, Template template2)
{
if (template1 is null)
{
if (template2 is null) return true;
else return false;
}
return template1.Equals(template2);
}
public static bool operator !=(Template template1, Template template2)
=> !(template1 == template2);
}
Используем его в классах модулей:
public class Module : IEquatable<Module>
{
public virtual Template Template { get; }
...
public Module(char symbol)
{
Symbol = symbol;
Template = new Template(typeof(Module), this);
}
...
}
public class ParametricModule<T> : Module, IEquatable<ParametricModule<T>>
{
public override Template Template { get; }
...
public ParametricModule(char symbol, T parameter) : base(symbol)
{
Parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
Template = new Template(typeof(T), this);
}
...
}
Вроде, работает как надо.
Как вариант, можно сделать класс шаблона вложенным в класс модуля. Это частично скроет его из видимости сторонних разработчиков.