繁体   English   中英

“显式接口实现的约束......”

[英]“Constraints for explicit interface implementation…”

我无法弄清楚为什么以下不会工作,任何想法? 公共接口IFieldSimpleItem {}

public interface IFieldNormalItem : IFieldSimpleItem
{ }

public class Person
{
    public virtual T Create<T>()
        where T : IFieldSimpleItem
    {
        return default(T);
    }
}

public class Bose : Person
{
    public override T Create<T>()
        where T : IFieldNormalItem //This is where the error is
    {
        return default(T);
    } 
}

我这样做的原因是,如果开发人员继承自Bose,Bose依赖于创建至少为IFieldNormalItem的实例。 以下仅依赖于IFieldSimpleItem,但上述内容应强制它至少为IFieldNormalItem。

public class Person
{
    public virtual IFieldSimpleItem Create() 
    {
        return null;
    }
}

public class Bose : Person
{
    public override IFieldSimpleItem Create()  
    {
        return null;
    } 
}

干杯安东尼

我很确定你使用编译器和泛型来节省一些运行时检查是不走运的。 您不能覆盖尚不存在的内容,并且您不能对相同的方法使用不同的返回类型。

我不能说我完全理解你的动机,但它有技术价值。

我的第一次尝试是使用具有非虚拟公共接口的基类,然后使用另一个受保护的虚拟方法CheckCreatedType ,它允许链中的任何东西在调用基类Create之前检查类型。

public class A
{
    public IFieldSimpleItem Create()
    {
        IFieldSimpleItem created = InternalCreate();
        CheckCreatedType(created);
        return created;
    }

    protected virtual IFieldSimpleItem InternalCreate()
    {
        return new SimpleImpl();
    }
    protected virtual void CheckCreatedType(IFieldSimpleItem item)
    { 
        // base class doesn't care. compiler guarantees IFieldSimpleItem
    }
}
public class B : A
{
    protected override IFieldSimpleItem InternalCreate()
    {
        // does not call base class.
        return new NormalImpl();
    }
    protected override void CheckCreatedType(IFieldSimpleItem item)
    {
        base.CheckCreatedType(item);
        if (!(item is IFieldNormalItem))
            throw new Exception("I need a normal item.");

    }
}

以下内容在运行时检查基类。 无法解决的问题是你仍然需要依赖被调用的基类方法。 行为不端的子类可以通过不调用base.CheckCreatedType(item)来中断所有检查。

替代方法是硬编码基类内所有子类的所有检查(坏),或以其他方式外部化检查。

尝试2 :(子)类注册他们需要的检查。

public class A
{
    public IFieldSimpleItem Create()
    {
        IFieldSimpleItem created = InternalCreate();
        CheckCreatedType(created);
        return created;
    }

    protected virtual IFieldSimpleItem InternalCreate()
    {
        return new SimpleImpl();
    }

    private void CheckCreatedType(IFieldSimpleItem item)
    {
        Type inspect = this.GetType();
        bool keepgoing = true;
        while (keepgoing)
        {
            string name = inspect.FullName;
            if (CheckDelegateMethods.ContainsKey(name))
            {
                var checkDelegate = CheckDelegateMethods[name];
                if (!checkDelegate(item))
                    throw new Exception("failed check");
            }
            if (inspect == typeof(A))
            {
                keepgoing = false;
            }
            else
            {
                inspect = inspect.BaseType;
            }
        }
    }

    private static Dictionary<string,Func<IFieldSimpleItem,bool>> CheckDelegateMethods = new Dictionary<string,Func<IFieldSimpleItem,bool>>();
    protected static void RegisterCheckOnType(string name, Func<IFieldSimpleItem,bool> checkMethod )
    {
        CheckDelegateMethods.Add(name, checkMethod);
    }
}
public class B : A
{
    static B()
    {
        RegisterCheckOnType(typeof(B).FullName, o => o is IFieldNormalItem);
    }

    protected override IFieldSimpleItem InternalCreate()
    {
        // does not call base class.
        return new NormalImpl();
    }
}

检查由子类注册一个委托在基类中调用,但没有基类知道所有规则。 另请注意,它仍然是非虚拟公共接口,它允许基类在返回结果之前检查结果。

我假设你正试图抓住这是一个开发人员错误。 如果它适用,您可以使用System.Diagnostics.Conditional("DEBUG")]装饰运行时检查方法,允许Release版本跳过检查。

我对泛型的了解并不完美,所以也许这是不必要的。 但是,这里的检查不一定是单独的类型:这可以适用于其他用途。 例如,在Register..中传递的委托不必仅仅检查引用是否是特定类型'

*请注意,如上所述,在类型名称上创建字典可能不太好; 这个工作有点简单,以说明使用的机制。

这是不允许的,因为它违反了Liskov Substitution Principle。

假设你有另一个界面:

public interface IFieldSuperItem : IFieldSimpleItem

然后你可能会这样做

Person p = new Boss();
p.Create<IFieldSuperItem>();

第二行中的调用,虽然与Create in Person的定义兼容,但显然与Boss中定义的不兼容(仅适用于IFieldNormalItem及其子类)。

我认为问题是你覆盖以前定义的方法。 因此,您有效地尝试更改方法的定义,这是不允许的。 您唯一的选择是创建一个新方法,例如

public class Bose : Person
{
    public virtual T CreateNormal<T>()
        where T : IFieldNormalItem //This is where the error is
    {
        return default(T);
    } 
}

或要求Person类上的普通字段,或动态进行验证。

您似乎无法更改方法的定义,但是您可以使您的类通用而不是创建方法吗?

public class Person<T> where T : IFieldSimpleItem
{
    public virtual T Create()
    {
        return default(T);
    }
}

public class Bose<T> : Person<T> where T : IFieldNormalItem
{
    public override T Create()
    {
        return default(T);
    } 
}

更改通用约束会更改方法签名,如果您覆盖虚拟,则不允许这样做。

我想你可能需要将Create方法拆分成一个单独的类:

public interface IFieldSimpleItem { }

public interface IFieldNormalItem : IFieldSimpleItem{ }

public interface IFieldCreator<TField, TPerson> where TField : IFieldSimpleItem where TPerson : Person
{
    TField Create(TPerson person);
}

public class Person
{
}

public class Bose : Person
{
}

public class PersonFieldCreator : IFieldCreator<IFieldSimpleItem, Person> 
{
    public IFieldSimpleItem Create(Person person) { return null; }
}

public class BoseFieldCreator : IFieldCreator<IFieldNormalItem, Bose>
{
    public IFieldNormalItem Create(Bose person) { return null; }
}

那这个呢:

public interface IFieldNormalItem : IFieldSimpleItem
{ }

public class Person<T> where T : IFieldSimpleItem
{
    public virtual T Create()
    {
        return default(T);
    }
}

现在你可以拥有Person<IFieldSimpleItem> (对应于Person )或Person<IFieldNormalItem> (对应于Bose )。

下面的代码足以覆盖。 类型T已经表示需要由基类Person中的IFieldSimpleItem实现。

public class Bose : Person
{
    public override T Create<T>()
        // where T : IFieldNormalItem // You don't need this line.
    {
        return default(T);
    } 
}

编辑:我完全错了这个问题,所以上面的代码不会解决这个问题。 你唯一要做的就是; 不要通过“覆盖”但“虚拟”覆盖Create方法。

public class Bose : Person
{
    public virtual T Create<T>()
        where T : IFieldNormalItem
    {
        return default(T);
    } 
}

最简单的例子是这打破了多态性。 如果你有一个Person的集合,其中一个或多个项目属于Bose类型,一旦它击中Bose就会崩溃。

Person[] people;
[...initialize this somewhere...]

foreach(Person p in people)
  p.Create<IFieldSimpleItem>();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM