简体   繁体   English

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

[英]“Constraints for explicit interface implementation…”

I can't figure out why the following wont work, any ideas?? 我无法弄清楚为什么以下不会工作,任何想法? public interface IFieldSimpleItem { } 公共接口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);
    } 
}

The reason why I am doing this is due to the fact that if a developer inherits from Bose, Bose relies on the instance being creating being at least of IFieldNormalItem. 我这样做的原因是,如果开发人员继承自Bose,Bose依赖于创建至少为IFieldNormalItem的实例。 Whereas the below only relies on it being IFieldSimpleItem but the above should force it to be at least IFieldNormalItem. 以下仅依赖于IFieldSimpleItem,但上述内容应强制它至少为IFieldNormalItem。

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

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

Cheers Anthony 干杯安东尼

I'm pretty sure you're out of luck as far as using the compiler and generics to save you some runtime checks. 我很确定你使用编译器和泛型来节省一些运行时检查是不走运的。 You can't override something that doesn't already exist, and you can't have different return types to the same methods. 您不能覆盖尚不存在的内容,并且您不能对相同的方法使用不同的返回类型。

I can't say I completely understand your motivation, but it has technical merit. 我不能说我完全理解你的动机,但它有技术价值。

My first attempt was using the base class having a Non-Virtual public interface, and then having another protected virtual method CheckCreatedType that would allow anything in the chain to inspect the type before the base class Create was called. 我的第一次尝试是使用具有非虚拟公共接口的基类,然后使用另一个受保护的虚拟方法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.");

    }
}

The following sticks in runtime checking at the base class. 以下内容在运行时检查基类。 The unresolvable issue is you still have to rely on the base class method being called. 无法解决的问题是你仍然需要依赖被调用的基类方法。 A misbehaving subclass can break all checks by not calling base.CheckCreatedType(item) . 行为不端的子类可以通过不调用base.CheckCreatedType(item)来中断所有检查。

The alternatives are you hardcode all the checks for all subclasses inside the base class (bad), or otherwise externalize the checking. 替代方法是硬编码基类内所有子类的所有检查(坏),或以其他方式外部化检查。

Attempt 2: (Sub)Classes register the checks they need. 尝试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();
    }
}

The check is done by the subclass registering a delegate to invoke in base class, but without the base class knowing all the rules upfront. 检查由子类注册一个委托在基类中调用,但没有基类知道所有规则。 Notice too that it's still the Non-Virtual public interface which allows the base class to check the results before returning them. 另请注意,它仍然是非虚拟公共接口,它允许基类在返回结果之前检查结果。

I'm assuming that it's a developer error that you're trying to catch. 我假设你正试图抓住这是一个开发人员错误。 If it's applicable, you can adorn the runtime check method with System.Diagnostics.Conditional("DEBUG")] , allowing the Release version to skip the checks. 如果它适用,您可以使用System.Diagnostics.Conditional("DEBUG")]装饰运行时检查方法,允许Release版本跳过检查。

My knowledge of generics isn't perfect, so maybe this is unnecessary. 我对泛型的了解并不完美,所以也许这是不必要的。 However the checks here don't have to be for type alone: this could be adapted for other uses. 但是,这里的检查不一定是单独的类型:这可以适用于其他用途。 eg the delegate passed in Register.. doesn't have to just check the reference is a specific type' 例如,在Register..中传递的委托不必仅仅检查引用是否是特定类型'

* Note that it's probably not good to create the dictionary on the type name as written above; *请注意,如上所述,在类型名称上创建字典可能不太好; this working is a little simplistic in order to illustrate the mechanism used. 这个工作有点简单,以说明使用的机制。

That's not allowed because it violates Liskov Substitution Principle. 这是不允许的,因为它违反了Liskov Substitution Principle。

Let's say you have another interface: 假设你有另一个界面:

public interface IFieldSuperItem : IFieldSimpleItem

You then might do this 然后你可能会这样做

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

The call in second line, while compatible with the definition of Create in Person but obviously not compatible to that defined in Boss (which only work with IFieldNormalItem and its subclass). 第二行中的调用,虽然与Create in Person的定义兼容,但显然与Boss中定义的不兼容(仅适用于IFieldNormalItem及其子类)。

I think the problem is that you override a previously defined method. 我认为问题是你覆盖以前定义的方法。 So effectively you try to change the definition of the method, which is not permitted. 因此,您有效地尝试更改方法的定义,这是不允许的。 Your only choice is to either create a new method, eg 您唯一的选择是创建一个新方法,例如

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

or require a normal field on the Person class, or do the validation dynamically. 或要求Person类上的普通字段,或动态进行验证。

It seems you can not change the method's definition but can you make your classes generic instead of the Create Method? 您似乎无法更改方法的定义,但是您可以使您的类通用而不是创建方法吗?

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);
    } 
}

Changing the generic constraint changes the method signature which is not allowed if you're overriding a virtual. 更改通用约束会更改方法签名,如果您覆盖虚拟,则不允许这样做。

I think you may need to split the Create method into a separate class: 我想你可能需要将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; }
}

What about this: 那这个呢:

public interface IFieldNormalItem : IFieldSimpleItem
{ }

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

Now you can have Person<IFieldSimpleItem> (corresponds to Person ) or Person<IFieldNormalItem> (corresponds to Bose ). 现在你可以拥有Person<IFieldSimpleItem> (对应于Person )或Person<IFieldNormalItem> (对应于Bose )。

The code below is enough to override. 下面的代码足以覆盖。 The type T is already indicated as need to be implemented by IFieldSimpleItem in base class Person. 类型T已经表示需要由基类Person中的IFieldSimpleItem实现。

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

EDIT : I totally got the question wrong so the code above won't solve this case. 编辑:我完全错了这个问题,所以上面的代码不会解决这个问题。 The only thing you have to do is; 你唯一要做的就是; not to override the Create method by "override" but "virtual". 不要通过“覆盖”但“虚拟”覆盖Create方法。

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

The simplest example is that this breaks polymorphism. 最简单的例子是这打破了多态性。 If you had a collection of Person, where one or more of those items is of the type Bose, this would crash as soon as it hits a Bose. 如果你有一个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