我无法弄清楚为什么以下不会工作,任何想法? 公共接口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;
}
}
干杯 安东尼
答案 0 :(得分:3)
对于使用编译器和泛型来节省运行时检查,我很确定你运气不好。您不能覆盖尚不存在的内容,并且您不能对相同的方法使用不同的返回类型。
我不能说我完全理解你的动机,但它有技术上的优点。
我的第一次尝试是使用具有非虚拟公共接口的基类,然后使用另一个受保护的虚拟方法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")]
来装饰运行时检查方法,允许发布版本跳过检查。
我对仿制药的了解并不完美,所以也许这是不必要的。但是,这里的检查不一定是单独的类型:这可以适用于其他用途。例如传递给Register..
的代表不必仅检查引用是否为特定类型'
*请注意,如上所述,在类型名称上创建字典可能不太好;为了说明所使用的机制,这项工作有点简单。
答案 1 :(得分:2)
这是不允许的,因为它违反了利斯科夫替代原则。
假设你有另一个界面:
public interface IFieldSuperItem : IFieldSimpleItem
然后你可以这样做
Person p = new Boss();
p.Create<IFieldSuperItem>();
第二行中的调用,虽然与Create in Person的定义兼容,但显然与Boss中定义的不兼容(仅适用于IFieldNormalItem及其子类)。
答案 2 :(得分:1)
我认为问题在于您覆盖以前定义的方法。因此,您有效地尝试更改方法的定义,这是不允许的。您唯一的选择是创建一种新方法,例如
public class Bose : Person
{
public virtual T CreateNormal<T>()
where T : IFieldNormalItem //This is where the error is
{
return default(T);
}
}
或要求Person类上的普通字段,或动态进行验证。
答案 3 :(得分:1)
您似乎无法更改方法的定义,但是您可以使您的类通用而不是创建方法吗?
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);
}
}
答案 4 :(得分:1)
更改通用约束会更改方法签名,如果您覆盖虚拟,则不允许这样做。
我认为您可能需要将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; }
}
答案 5 :(得分:0)
这个怎么样:
public interface IFieldNormalItem : IFieldSimpleItem
{ }
public class Person<T> where T : IFieldSimpleItem
{
public virtual T Create()
{
return default(T);
}
}
现在,您可以Person<IFieldSimpleItem>
(对应Person
)或Person<IFieldNormalItem>
(对应Bose
)。
答案 6 :(得分:0)
以下代码足以覆盖。类型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);
}
}
答案 7 :(得分:0)
最简单的例子就是打破了多态性。如果你有一个Person集合,其中一个或多个这些项目属于Bose类型,那么一旦它击中Bose就会崩溃。
Person[] people;
[...initialize this somewhere...]
foreach(Person p in people)
p.Create<IFieldSimpleItem>();