The title is quite a mouthful, so here is the problem spelled out:
(please note - my question is how to implement point 6, and if you look at my code attempting to do this, it might look like a duplicate of an existing frequently-asked question for which the answer is "this conversion is impossible - this can't be done", however I know this already, so what I am looking for is some other way to solve point 6)
Everything is working, except my implementation of point #6. My attempt is below, but fails to compile. The error is in the SkuCodeField constructor's call to the base constructor. The error is:
cannot convert from 'Contracts.IBusinessFieldValidator<Implementations.SkuCodeField, string>' to 'Contracts.IBusinessFieldValidator<Contracts.BusinessInputFieldTypeBase<Implementations.SkuCodeField, string>, string>'
Please note (as I mention above) I do know that my attempted conversion to the type required by the base constructor cannot succeed ( as noted in other questions ). This code is here because I need to show you a minimal workable example (mwe).
My question is - is it possible to define (in an abstract base class) a constraint that refers to the derived type?
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
// Example usage
var field = new Implementations.SkuCodeField(new Implementations.Ean13CodeValidator());
var input = Console.ReadLine();
var inputOk = field.SetValue(input);
Console.WriteLine(inputOk);
}
}
}
namespace Contracts
{
public interface IBusinessInputFieldType<TV>
where TV : IComparable, IConvertible, IEquatable<TV> // this is a way to limit TV to value types and strings
{
TV SelectedValue { get; }
bool SetValue(TV value);
}
public abstract class BusinessInputFieldTypeBase<TV> : IBusinessInputFieldType<TV>
where TV : IComparable, IConvertible, IEquatable<TV>
{
protected BusinessInputFieldTypeBase(IBusinessFieldValidator<IBusinessInputFieldType<TV>, TV> validator)
{
_validator = validator;
}
private readonly IBusinessFieldValidator<IBusinessInputFieldType<TV>, TV> _validator;
public TV SelectedValue { get; private set; }
public bool SetValue(TV value)
{
var priorValue = SelectedValue;
SelectedValue = value;
if (_validator != null)
{
var valueOk = _validator.ValidateValue(this);
if (!valueOk) SelectedValue = priorValue;
return valueOk;
}
return true;
}
}
public interface IBusinessFieldValidator<TF, TV>
where TF : IBusinessInputFieldType<TV>
where TV : IComparable, IConvertible, IEquatable<TV>
{
bool ValidateValue(TF field);
}
}
namespace Implementations
{
public class SkuCodeField : Contracts.BusinessInputFieldTypeBase<string>
{
public SkuCodeField(Contracts.IBusinessFieldValidator<SkuCodeField, string> validator)
: base(validator)
// the below complies but the validator is passed through as null
//: base(validator as Contracts.IBusinessFieldValidator<Contracts.IBusinessInputFieldType<string>, string>)
{
}
}
public class Ean13CodeValidator : Contracts.IBusinessFieldValidator<SkuCodeField, string>
{
public bool ValidateValue(SkuCodeField field)
{
return field != null && field.SelectedValue != null && field.SelectedValue.Length == 13;
}
}
public class Ean8CodeValidator : Contracts.IBusinessFieldValidator<SkuCodeField, string>
{
public bool ValidateValue(SkuCodeField field)
{
return field != null && field.SelectedValue != null && field.SelectedValue.Length == 8;
}
}
}
Credit for this answer is due to @JeremyLakeman who pointed me to the solution below (see comments in my post).
He said - To solve the problem BusinessInputFieldTypeBase needs a "curiously recursive" generic constraint like BusinessInputFieldTypeBase<TF,TV> ... where T:BusinessInputFieldTypeBase<TF,TV> then SkuCodeField : Contracts.BusinessInputFieldTypeBase<SkuCodeField, string>. This way the base type can enforce that the validator can process, not itself or any other string field, but SkuCodeField specifically.
The next part was to modify the base constructor to require a slightly different type: protected BusinessInputFieldTypeBase(IBusinessFieldValidator<TF, TV> validator)
The complete working code sample is:
using System;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
// Example usage
var field = new Implementations.SkuCodeField(new Implementations.Ean13CodeValidator());
var input = Console.ReadLine();
var inputOk = field.SetValue(input);
Console.WriteLine(inputOk);
}
}
}
namespace Contracts
{
public interface IBusinessInputFieldType<TV>
where TV : IComparable, IConvertible, IEquatable<TV> // this is a way to limit TV to value types and strings
{
TV SelectedValue { get; }
bool SetValue(TV value);
}
public abstract class BusinessInputFieldTypeBase<TF, TV> : IBusinessInputFieldType<TV>
where TF : BusinessInputFieldTypeBase<TF, TV> // "curiously recursive" generic constraint
where TV : IComparable, IConvertible, IEquatable<TV>
{
protected BusinessInputFieldTypeBase(IBusinessFieldValidator<TF, TV> validator)
{
_validator = validator;
}
private readonly IBusinessFieldValidator<TF, TV> _validator;
public TV SelectedValue { get; private set; }
public bool SetValue(TV value)
{
var priorValue = SelectedValue;
SelectedValue = value;
if (_validator != null)
{
var valueOk = _validator.ValidateValue(this as TF);
if (!valueOk) SelectedValue = priorValue;
return valueOk;
}
return true;
}
}
public interface IBusinessFieldValidator<TF, TV>
where TF : IBusinessInputFieldType<TV>
where TV : IComparable, IConvertible, IEquatable<TV>
{
bool ValidateValue(TF field);
}
}
namespace Implementations
{
public class SkuCodeField : Contracts.BusinessInputFieldTypeBase<SkuCodeField, string>
{
public SkuCodeField(Contracts.IBusinessFieldValidator<SkuCodeField, string> validator)
: base(validator)
{
}
}
public class Ean13CodeValidator : Contracts.IBusinessFieldValidator<SkuCodeField, string>
{
public bool ValidateValue(SkuCodeField field)
{
return field != null && field.SelectedValue != null && field.SelectedValue.Length == 13;
}
}
public class Ean8CodeValidator : Contracts.IBusinessFieldValidator<SkuCodeField, string>
{
public bool ValidateValue(SkuCodeField field)
{
return field != null && field.SelectedValue != null && field.SelectedValue.Length == 8;
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.