访问自定义类的 IEnumerable<T> 时出现 NullReferenceException [重复]

Posted

技术标签:

【中文标题】访问自定义类的 IEnumerable<T> 时出现 NullReferenceException [重复]【英文标题】:NullReferenceException when accessing an IEnumerable<T> of a custom class [duplicate] 【发布时间】:2020-08-13 21:22:24 【问题描述】:

目标

我正在使用 Unity 设计一个boid 系统。当它进入对撞机时,我通过在“Swarm”列表中添加一个 boid 来处理感知半径。为了找到每个 boid 的力,我需要循环遍历 swarm 列表,访问“Boid”类,并检索速度和位置。

问题

来自每个 swarm 实体的 Boid 类被添加到一个新列表中,并传递给物理控制器。但是,在第 96 行抛出了 NullReferenceException,我不明白为什么该变量会为空。据我所知,使用 foreach 访问填充的 Enumerable&lt;Boid&gt; 应该包含变量。

NullReferenceException:对象引用未设置为对象的实例 Boid.Alignment (System.Collections.Generic.IEnumerable`1[T] boids) (在 Assets/Scripts/Boid.cs:96) Boid.Update() (在 Assets/Scripts/Boid.cs:42)

经过测试,似乎在访问新的 Boid 列表的任何部分时都会抛出它。

为什么我的新列表没有数据?有没有更好的方法来处理 3D 空间中的 boid 的 2D 实现?有没有我可以用来更好地理解 Linq 的资源?

附:我对使用 Linq 系统非常陌生,大部分代码取自 this video 和 this Unity project script

代码

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class Boid : MonoBehaviour

// Global Variables
public Boid_Settings settings;

// Local Variables
public Rigidbody body;
public Vector2 acceleration;
public Vector2 velocity

    get
     return new Vector2(body.velocity.x, body.velocity.z); 
    set
     body.velocity = new Vector3(value.x, body.velocity.y, value.y); 

public Vector2 position

    get
     return new Vector2(transform.position.x, transform.position.z); 

public List<GameObject> swarm = new List<GameObject>();
public List<GameObject> targets = new List<GameObject>();


// Functions
private void Start()

    float angle = Random.Range(0, 2 * Mathf.PI);
    transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
    velocity = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));


private void Update()

    IEnumerable<Boid> boids = swarm.Select(o => o.GetComponent<Boid>()).ToList(); //Line 40

    Vector2 alignment = Alignment(boids); //LINE 42
    Vector2 separation = Separation(boids);
    Vector2 cohesion = Cohesion(boids);

    acceleration = settings.alignmentWeight * alignment + settings.cohesionWeight * cohesion + settings.seperationWeight * separation;

    UpdatePhysics();


// Entity Awareness Assignment
private void OnTriggerEnter(Collider collider)

    if (collider.CompareTag("Zombie"))
     swarm.Add(collider.gameObject); 
    else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
     targets.Add(collider.gameObject); 


private void OnTriggerExit(Collider collider)

    if (collider.CompareTag("Zombie"))
     swarm.Remove(collider.gameObject); 
    else if (collider.CompareTag("Player") || collider.CompareTag("Lure"))
    
        targets.Remove(collider.gameObject);
        StartCoroutine(LingerTarget(collider.gameObject));
    


IEnumerator LingerTarget(GameObject target)

    targets.Add(target);
    yield return new WaitForSeconds(settings.lingerTime);
    targets.Remove(target);



// Core Boid Logic
public void UpdatePhysics()

    // Apply the acceleration, and then limit the speed to the maximum.
    Vector2 UncappedVelocity = velocity + acceleration;
    velocity = ApplyLimit(UncappedVelocity, settings.maxSpeed);

    float angle = Mathf.Atan2(velocity.y, velocity.x) * Mathf.Rad2Deg;
    body.transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));


private Vector2 Alignment(IEnumerable<Boid> boids)

    Vector2 velocity = Vector2.zero;
    if (!boids.Any()) return velocity;

    foreach (Boid boid in boids)
     velocity += boid.velocity;  //LINE 96
    velocity /= boids.Count();

    Vector2 steer = Steer(velocity.normalized * settings.maxSpeed);
    return steer;


private Vector2 Cohesion(IEnumerable<Boid> boids)

    if (!boids.Any()) return Vector2.zero;

    Vector2 sumPositions = Vector2.zero;
    foreach (Boid boid in boids)
     sumPositions += boid.position; 
    Vector2 average = sumPositions / boids.Count();
    Vector2 direction = average - position;

    Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
    return steer;


private Vector2 Separation(IEnumerable<Boid> boids)

    Vector2 direction = Vector2.zero;
    boids = boids.Where(o => Vector3.Distance(o.transform.position, position) <= settings.avoidanceRadius);
    if (!boids.Any()) return direction;

    foreach (Boid boid in boids)
    
        Vector2 difference = position - boid.position;
        direction += difference.normalized / difference.magnitude;
    
    direction /= boids.Count();

    Vector2 steer = Steer(direction.normalized * settings.maxSpeed);
    return steer;


private Vector2 Steer(Vector2 desired)

    Vector2 steer = desired - velocity;
    steer = ApplyLimit(steer, settings.maxSteerForce);

    return steer;


// Calculation Helpers
private Vector2 ApplyLimit(Vector2 baseVector, float limit)

    if (baseVector.sqrMagnitude > limit * limit)
     baseVector = baseVector.normalized * limit; 
    return baseVector;


Boid_Settings 模块:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

[CreateAssetMenu]
public class Boid_Settings : ScriptableObject

// Boid
public float maxSpeed = 5;
public float avoidanceRadius = 1;
public float maxSteerForce = 3;

public float lingerTime = 2.5f;
public float alignmentWeight = 1;
public float cohesionWeight = 1;
public float seperationWeight = 1;
public float targetWeight = 1;

// Spawner
public float awarenessRadius = 2.5f;

上下文图片

证明 boid 类有数据要读取 Unity Enviroment & Boid.cs 附件 unity Boid Boid.body 组件 游戏运行时,每个 Boid 都会找到两个群体伙伴

【问题讨论】:

检查 Boid.cs 行号 96。 那是错误发生的地方,但我不明白为什么。 @Sove67 从异常堆栈跟踪中可以看出,当您将变量 boids 传递给 Alignment(boids); 时,Update() 方法中出现了错误。在这一行设置一个断点,并检查boids是否是null上一行IEnumerable&lt;Boid&gt; boids = swarm.Select(o =&gt; o.GetComponent&lt;Boid&gt;()).ToList(); null @RyanWilson 我添加了 if (boids == null) Debug.Log("No Data Attached"); 在 Alignment(boids) 之前,但它没有被调用。 这能回答你的问题吗? What is a NullReferenceException, and how do I fix it? 也检查了body 不为空吗? 【参考方案1】:

原来我分配了空数据,因为第 40 行的 GetComponent 函数找到了 boid 的“Body”,它没有附加 Boid 脚本。使用 GetComponentInChildren 搜索时,会填充数组并给出值。

旧线:

IEnumerable&lt;Boid&gt; boids = swarm.Select(o =&gt; o.GetComponent&lt;Boid&gt;()).ToList();

新线:

IEnumerable&lt;Boid&gt; boids = swarm.Select(o =&gt; o.GetComponentInChildren&lt;Boid&gt;()).ToList();

【讨论】:

以上是关于访问自定义类的 IEnumerable<T> 时出现 NullReferenceException [重复]的主要内容,如果未能解决你的问题,请参考以下文章

IEnumerable<T>是啥意思?

使用 Linq 选择类的属性以返回 IEnumerable<T>

为啥 Enumerable 不继承自 IEnumerable<T>

将 DataTable 转换为 IEnumerable<T>

IEnumerable<T> 使用 c# 在函数内访问 T 的属性

List<T> 派生自 IList<T> 和 IEnumerable<T>,没用?