.net 如果C#接口的属性共享目标类的名称,并且属性为“init”,则无法在构造函数中分配属性

nc1teljy  于 2023-05-02  发布在  .NET
关注(0)|答案(2)|浏览(250)

我有一个名为IHierarchable的接口,用于“Entity”类和“Scene”类,后者作为所有实体的根。

/// <summary>
/// Signifies that an object can exist in the game hierarchy and can have children.
/// </summary>
public interface IHierarchable
{
    public abstract Scene Scene { get; protected init; }

    /// <summary>
    /// The parent of this object.
    /// </summary>
    public abstract IHierarchable Parent { get; protected set; }

    /// <summary>
    /// Any children belongong to this object.
    /// </summary>
    public abstract ReadOnlyCollection<IHierarchable> Children { get; protected set; }
}

但是当我在Scene类上实现这个时:

/// <summary>
/// A base 2D world, into which <see cref="Entity"/> instances can be placed.
/// </summary>
public class Scene : IHierarchable
{
    Scene IHierarchable.Scene { get; init; }
    public IHierarchable Parent { get; set; }
    public ReadOnlyCollection<IHierarchable> Children { get; set; }

    public Scene()
    {
        (this as IHierarchable).Scene = null;
    }
}

我在构造函数中有以下错误:
仅初始化属性或索引器“IHierarchable”。只能在对象初始值设定项中,或在示例构造函数或init访问器中的this或base上分配Scene
我必须在这个类中显式地实现接口,因为'Scene'属性碰巧共享'Scene'类的名称。但是,一旦我以这种方式实现它,我就无法实际设置该属性,即使我在Scene的构造函数中设置它并使用'this'关键字,就像错误消息所建议的那样。

oxiaedzo

oxiaedzo1#

您定义的interface类型using DIM syntax不是您想要的。我很理解这是造成混乱的一个重要原因,特别是它也是一个非常新的语言特性。
DIM属性的一个大问题是,interface类型(包括DIM接口)不能有字段,因此不能有自动属性,因此当在DIM接口中使用C#class/struct自动属性语法时,实际上只是定义了一个类似abstract的属性

  • 首先,将interface类型更改为 good old ' fashionedinterface
  • 另外,将SceneParent设置为可空属性,因为我假设最上面的IHierarchable对象最终会有一个null父对象,并且您显式地设置了Scene = null,所以我假设这是故意的。
  • Fun-fact:you should never need to explicitly set a field or field-backed auto-property to null or default-这是CLR中隐含的。
  • 我假设您的IHierarchable旨在成为其消费者的只读接口(即。例如,IHierarchable的消费者不应该能够覆盖任何属性-并且接口的init成员没有意义,因为您不能对interface类型使用(后构造)对象初始化器语法。
  • 因为Childrenis a collection property it should not have a set accessor-仅get-并且使用 * 协变集合接口 * 而不是具体类型(即即使用IReadOnlyList<out T>而不是ReadOnlyCollection<T>),这样就可以合法地将Children作为,e.例如IEnumerable<IHierarchable>IReadOnlyCollection<Object?>,而不需要不安全的强制转换或创建.ToList()副本。使用IReadOnly...<T>接口还意味着集合的接收者不能意外地修改它,这是软件错误的另一个常见原因。
  • 就像这样:
public interface IHierarchable
{
    Scene? Scene { get; }

    /// <summary>The parent of this object.</summary>
    IHierarchable? Parent { get; }

    /// <summary>Any children belongong to this object.</summary>
    IReadOnlyList<IHierarchable> Children { get; }
}
  • 然后实现interface。..
  • 构造函数的存在是有原因的,使用它们来实现class invariants(但从你的帖子中,我无法判断你是否需要一个)。
  • 如果SceneScene属性总是为null,那么将其设置为字段支持的自动属性是没有意义的--只需将其设置为始终返回null的仅get属性即可
  • 除非你完全知道你需要子类化它,否则就创建sealed类,因为子类化和显式接口实现(如Scene? IHierarchable.Scene => null;)不能很好地结合在一起。
public sealed class Scene : IHierarchable
{
    public Scene()
    {
        // Nothing needs to go here - unless it does?
    }

    public IHierarchable? Parent { get; }

    public IReadOnlyList<IHierarchable> Children { get; } = new List<IHierarchable>();

    Scene? IHierarchable.Scene => this;
    // or return null:
    Scene? IHierarchable.Scene => null;
}
wz3gfoph

wz3gfoph2#

1.不需要显式地将Scene分配给null
1.如果您仍然想指定它,可以使用属性初始化语法:

public class Scene : IHierarchable
{
    Scene IHierarchable.Scene { get; init; } = null;
    public IHierarchable Parent { get; set; }
    public ReadOnlyCollection<IHierarchable> Children { get; set; }

    public Scene()
    {
    }
}

1.最后,如果两者都不适合你-重命名属性或类以摆脱显式接口实现处理:

public interface IHierarchable
{
    Scene Scene1 { get; init; } // Just for example, can be something more meaningful
    // ...
}

public class Scene : IHierarchable
{
    public Scene Scene1 { get; init; } 
    // ...

    public Scene()
    {
        Scene1 = null;
    }
}

附言
1.考虑使用nullable reference typesScene Scene1-〉Scene? Scene1
1.请注意,接口中的protected修饰符可能并不意味着您认为它的作用,请参阅默认接口方法文档的接口中的修饰符部分。

相关问题