代码之家  ›  专栏  ›  技术社区  ›  Daniel

如何在类似名称空间的结构中分离类字段?

c#
  •  0
  • Daniel  · 技术社区  · 3 年前

    我有这节课

    public class Weapon
    {
        //default settings
        public float[] defaultEnergy;
        public UltEvent<Weapon>[] defaultCallbacks;
        public MotionType[] defaultMotions;
    
        //special settings
        public float[] specialEnergy;
        public UltEvent<Weapon>[] specialCallbacks;
        public MotionType[] specialMotions;
    }
    

    我想在我的代码中以一种更有组织的方式将默认设置和特殊设置分开。

    如果我需要默认能量,我需要写 weapon.defaultEnergy[i] .

    对于组织,我希望有一个类似于名称空间访问的语法。

    我想写 weapon.default.energy[i] 为了清楚起见,我指的是默认设置(特殊设置也是如此)。

    我可以在不创建新类的情况下使用此语法吗?

    1 回复  |  直到 3 年前
        1
  •  2
  •   Nin    3 年前

    如何添加这些:

    public (float[] Energy, UltEvent<Weapon>[] CallBacks, MotionType[] Motions) Default =>
            (defaultEnergy, defaultCallbacks, defaultMotions);
    public (float[] Energy, UltEvent<Weapon>[] CallBacks, MotionType[] Motions) Special =>
            (specialEnergy, specialCallbacks, specialMotions);
    

    更改后:

    public class Weapon
    {
        //default settings
        public float[] defaultEnergy;
        public UltEvent<Weapon>[] defaultCallbacks;
        public MotionType[] defaultMotions;
    
        //special settings
        public float[] specialEnergy;
        public UltEvent<Weapon>[] specialCallbacks;
        public MotionType[] specialMotions;
    
        public (float[] Energy, UltEvent<Weapon>[] CallBacks, MotionType[] Motions) Default =>
            (defaultEnergy, defaultCallbacks, defaultMotions);
        public (float[] Energy, UltEvent<Weapon>[] CallBacks, MotionType[] Motions) Special =>
            (specialEnergy, specialCallbacks, specialMotions);
    }
    

    然后,我们可以这样访问:

    public static void Main()
    {
        var weapon = new Weapon();
        weapon.defaultEnergy = new float[] {0, 1, 2};
    
        // access
        Console.WriteLine(weapon.Default.Energy[1]);
    }
    
        2
  •  1
  •   Dai    3 年前

    您可以通过两种不同的方法来做您正在做的事情(两种方法的运行时成本都是最低到零),但这两种方法都需要定义新的类型:

    选项1:呈现对象的零成本视图 readonly struct :

    • 只读结构 C#中的类型,它们只是在方法中传递或用作值(即,仅用作参数、局部值或返回值,但是 曾经用作中的字段 class )不会分配GC堆(或者,您可以使用 ref struct 真正地 确保没有GC堆使用,但是 引用结构 类型施加它们自己的约束,这在这种情况下可能是不希望的)。
    • 拥有这样的房产 => new MyStructType() 不涉及GC堆,尽管使用 new 操作员,顺便说一句。

    就像这样:

    public readonly struct WeaponDefaultSettings
    {
        private readonly Weapon w;
    
        internal WeaponDefaultSettings( Weapon w )
        {
            this.w = w;
        }
    
        public float[]            DefaultEnergy    => this.w.DefaultEnergy;
        public UltEvent<Weapon>[] DefaultCallbacks => this.w.DefaultCallbacks ;
        public MotionType[]       DefaultMotions   => this.w.DefaultMotions;
    }
    
    public readonly struct WeaponSpecialSettings
    {
        private readonly Weapon w;
    
        internal WeaponSpecialSettings( Weapon w )
        {
            this.w = w;
        }
    
        public float[]            SpecialEnergy    => this.w.SpecialEnergy;
        public UltEvent<Weapon>[] SpecialCallbacks => this.w.SpecialCallbacks;
        public MotionType[]       SpecialMotions   => this.w.SpecialMotions;
    }
    
    public class Weapon
    {
        public Weapon( ... )
        {
            this.DefaultEnergy = ?
            this.DefaultCallbacks = ?
            // etc
        }
    
        public WeaponDefaultSettings DefaultSettings => new WeaponDefaultSettings( this );
        public WeaponSpecialSettings SpecialSettings => new WeaponSpecialSettings( this );
    
        public float[]            DefaultEnergy    { get; } = Array.Empty<float>();
        public UltEvent<Weapon>[] DefaultCallbacks { get; } = ...?
        public MotionType[]       DefaultMotions   { get; } = Array.Empty<MotionType>();
    
        public float[]            SpecialEnergy    { get; } = Array.Empty<float>();
        public UltEvent<Weapon>[] SpecialCallbacks { get; } = ...?
        public MotionType[]       SpecialMotions   { get; } = Array.Empty<MotionType>();
    }
    

    用法:

    Weapon weapon = ...
    
    Console.WriteLine( weapon.DefaultSettings.DefaultEnergy );
    Console.WriteLine( weapon.SpecialSettings.SpecialEnergy );
    

    选项2:使用 interfaces 表示同一对象的不同视图

    …与C#8.0 DIM相结合,允许接口也有实现。

    因为这些接口是由单个对象实例实现和返回的,所以这种方法不需要任何额外的GC堆分配。而如果 Weapon 委派 任何到不同类型的接口,则必须 -即使实现是 struct (作为 structs 作为通过 interface 类型将被移动到GC堆)。

    public interface IHasWeaponDefaultSettings
    {
        Weapon Self { get; }
    
        public float[]            Energy    => this.Self.DefaultEnergy;
        public UltEvent<Weapon>[] Callbacks => this.Self.DefaultCallbacks;
        public MotionType[]       Motions   => this.Self.DefaultMotions;
    }
    
    public interface IHasWeaponSpecialSettings
    {
        Weapon Self { get; }
    
        public float[]            Energy    => this.Self.SpecialEnergy;
        public UltEvent<Weapon>[] Callbacks => this.Self.SpecialCallbacks;
        public MotionType[]       Motions   => this.Self.SpecialMotions;
    }
    
    public class Weapon: IHasWeaponDefaultSettings, IHasWeaponSpecialSettings
    {
        private float[]            defaultEnergy;
        private UltEvent<Weapon>[] defaultCallbacks;
        private MotionType[]       defaultMotions;
    
        private float[]            specialEnergy;
        private UltEvent<Weapon>[] specialCallbacks;
        private MotionType[]       specialMotions;
    
        Weapon IHasWeaponDefaultSettings.Self => this;
        Weapon IHasWeaponSpecialSettings.Self => this;
    
        public IHasWeaponDefaultSettings DefaultSettings => this;
        public IHasWeaponSpecialSettings SpecialSettings => this;
    }
    
    Weapon weapon = ...
    
    Console.WriteLine( weapon.DefaultSettings.Energy );
    Console.WriteLine( weapon.SpecialSettings.Energy );