例如我们有课程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;
//...
}
有没有更好的方法来做到这一点?
答案 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;
}
}