无代码耦合从另一个类读取对象的最佳方法

时间:2018-01-19 00:17:35

标签: c# unity3d

例如我们有课程Foo

class Foo {

  public class Physics {
    float gravity = 9.8f;
    float seperateDistance = 10f;
  }

  public Physics physics;

  void Start(){
    physics = new Physics();
    Bar baz = AddComponent<Bar>(); 
  }

}

同时,在Bar组件中,我们试图在gravity的物理内部获取Foo值。

目前这是我能想到的唯一方式。我不知怎的感觉不好。如果删除了课程Foo,则Bar将无法使用,我认为这不是一个好习惯。

我们在Foo中创建的baz内部:

void Start(){
       //...
  float gravity = getComponent<Foo>().physics.gravity;
      //...
}

有没有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:0)

这通常通过属性来完成,这些属性基本上是两个方法的对,即getter和setter方法。

要使用它们,只需通过public Physics physics;兑换public Physics physics { get; set; }即可。无需更改消耗代码,因为属性是像C#中的字段一样访问的。但是,属性还允许您自定义getter和setter。好处是这样的修改对客户端代码是透明的,因此您可以更改对该属性的访问权限,而无需更改使用该属性的代码。此外,属性可以是虚拟的或抽象的,并且可以作为接口的一部分。

答案 1 :(得分:0)

如果我正确地读了你,那么你担心的问题就是课堂耦合。正如格奥尔格的回答所说,你应该使用属性,而不是字段。但是你可以通过在Foo实现的Bar中注入一个接口来避免类耦合。这意味着您不需要Foo代码栏:

public interface IGravitySource
{
    double Gravity { get; }
}

public sealed class Foo : IGravitySource
{
    private readonly Physics myPrivatePhysics = new Physics();

    private sealed class Physics
    {
        public double Gravity { get; } = 9.81;

        public double Smoothness { get; } = 0.05;
    }

    public double Gravity => myPrivatePhysics.Gravity;
}

public sealed class Bar
{
    private readonly IGravitySource gravitySource;

    public Bar(IGravitySource gravitySource)
    {
        this.gravitySource = gravitySource;
    }

    public void Start()
    {
        //...
        var gravity = gravitySource.Gravity;
        gravity += 1;
        //...
    }
}

编辑:

从技术上讲,您可能通过在多个地方有效地重新实施IGravitySource的属性来引入债务。对此的一个解决方案是让IGravitySource只有一个成员IGravity。这样,如果你决定用方向扩展IGravity,你就不需要改变Foo的实现:

public interface IGravitySource
{
    IGravity Gravity { get; }
}

internal interface IGravity
{
    double AccelerationDueToGravity { get; }
}

public sealed class Foo : IGravitySource
{
    private readonly Physics myPrivatePhysics = new Physics();

    private sealed class Physics : IGravity
    {
        public double AccelerationDueToGravity { get; } = 9.81;

        public double Smoothness { get; } = 0.05;
    }

    public IGravity Gravity => myPrivatePhysics;
}

public sealed class Bar
{
    private readonly IGravitySource gravitySource;

    public Bar(IGravitySource gravitySource)
    {
        this.gravitySource = gravitySource;
    }

    public void Start()
    {
        //...
        var gravity = gravitySource.Gravity.AccelerationDueToGravity;
        gravity += 1;
        //...
    }
}

答案 2 :(得分:0)

您可以检查是否附加了Foo组件

void Start() {
    //Check if there's Foo, if no then give them default to 3;
    float gravity = GetComponent<Foo>() ? GetComponent<Foo>().physics.gravity : 3;
    //...
}

或者您也可以将RequireComponentAttribute添加到类Bar,以便Foo始终绑定到Bar RequireComponentAttribute

但如果你的意思是“如果Foo Class(不是组件)不再存在怎么办? 我建议你像Adam Brown那样做方法注射。

但是当课程不再存在时,还有另一种解决方案。 这是我的想法。

创建一个特殊的属性来确定哪个字段是可共享的。然后得到使用该属性的字段。

示例: 当然,这还没有经过测试,但从理论上说它会起作用。

//Some special attribute 
[AttributeUsage(AttributeTargets.Field)]
public class ShareFieldAttribute : Attribute {

}

class Foo {

    public class Physics {
        float gravity = 9.8f;
        float seperateDistance = 10f;
    }

    //mark ShareField attribute to this field
    [ShareField]
    public Physics physics;

    void Start(){

        physics = new Physics();
        Bar baz = AddComponent<Bar>(); 
    }

}

class Bar {
    void Start() {
        //Get the field 'public Physics physics' by name and Type
        Physics physics = GetShareField<Physics>("physics", null);
        float gravity = physics ? physics.gravity : 3;
        //...
    }

    //Method Helper
    public T GetShareField<T>(string fieldName, T defaultValue)
    {
        foreach(var c in GetComponents<MonoBehaviour>())
        {
            var fields = c.GetType().GetFields().Where(field => field.FieldType == T && field.Name == fieldName && field.IsDefined(typeof(ShareFieldAttribute), false));
            return (T)fields[0].GetValue(c);
        }

        return defaultValue;
    }
}