C#
list
Unity
extension

範囲外参照を起こさずループさせてアクセス

あそび

範囲外参照を起こさずループさせてアクセス

範囲外参照せずにループさせてアクセスできる関数を作りました.
無駄にマイナスにも対応しているので要素数が0でない限り範囲外参照を起こしません.

ListExtension.cs
using System;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Listの拡張クラス
/// </summary>
public static class ListExtension
{
    //=================================================================================
    //ループ取得
    //=================================================================================

    /// <summary>
    /// 指定したインデックスの値を範囲外参照せずに繰り返しで返す
    /// </summary>
    public static T LoopElementAt<T>(this IList<T> list, int index)
    {
        if (list.Count == 0) throw new ArgumentException("要素数が0のためアクセスできません");

        // 分配
        index %= list.Count;

        // 正の値にずらす
        if (index < 0)
        {
            index += list.Count;
        }

        T target = list[index];

        return target;
    }
}

使用例

インクリメントするだけで簡単に最後の要素から最初の要素に飛べたりします.
ボタンの選択とかに使える…??

ListExtensionTest.cs
using UnityEngine;

public class ListExtensionTest : MonoBehaviour
{
    private readonly int[] _numbers = {0, 1, 2, 3};

    private int _index;

    private void Update()
    {
        Debug.Log("プラス側:" + _numbers.LoopElementAt(_index)); // 0 1 2 3 0 1 2 3...

        Debug.Log("マイナス側:" + _numbers.LoopElementAt(-_index)); // 0 3 2 1 0 3 2 1...

        _index++;
    }
}

新しいクラスを作る

アイデアをいただいたので、別の方法としてインデクサーを使ってループさせたアクセスを作ってみることにしました。

ListExtension.cs
using System;
using System.Collections.Generic;

/// <summary>
///     Listの拡張クラス
/// </summary>
public static class ListExtension
{
    //=================================================================================
    //ループ取得
    //=================================================================================

    /// <summary>
    ///     指定したインデックスの値を範囲外参照せずに繰り返しで返す
    /// </summary>
    public static T GetLoopElementAt<T>(this IList<T> list, int index)
    {
        if (list.Count == 0) throw new ArgumentException("要素数が0のためアクセスできません");

        // 分配
        index %= list.Count;

        // 正の値にずらす
        if (index < 0)
            index += list.Count;

        var target = list[index];

        return target;
    }

    /// <summary>
    ///     指定したインデックスの値を範囲外参照せずに繰り返しで参照し,代入する
    /// </summary>
    public static void SetLoopElementAt<T>(this IList<T> list, int index, T value)
    {
        if (list.Count == 0) throw new ArgumentException("要素数が0のためアクセスできません");

        // 分配
        index %= list.Count;

        // 正の値にずらす
        if (index < 0)
            index += list.Count;

        list[index] = value;
    }

    /// <summary>
    ///     ループリストに変換
    /// </summary>
    public static LoopList<T> ToLoopList<T>(this IList<T> list)
    {
        return new LoopList<T>(list);
    }
}


/// <summary>
///     指定したインデックスの値を繰り返しで参照する
/// </summary>
public class LoopList<T>
{
    private readonly IList<T> _list;

    public LoopList(IList<T> list)
    {
        _list = list;
    }

    public T this[int index]
    {
        get { return _list.GetLoopElementAt(index); }
        set { _list.SetLoopElementAt(index, value); }
    }

    /// <summary>
    ///     IListに変換
    /// </summary>
    public IList<T> ToIList()
    {
        return _list;
    }
}
ListExtensionTest.cs
using System.Collections.Generic;
using UnityEngine;

public class ListExtensionTest : MonoBehaviour
{
    private readonly LoopList<int> _numberLoopList = new LoopList<int>(new List<int> {0, 1, 2, 3});

    private int _index;

    private void Update()
    {
        Debug.Log("プラス側:" + _numberLoopList[_index]); // 0 1 2 3 0 1 2 3...

        Debug.Log("マイナス側:" + _numberLoopList[-_index]); // 0 3 2 1 0 3 2 1...

        _index++;
    }
}

これで,[]を使ってアクセスできるようになりました.ついでにToList(),ToLoopList()で行き来できるようにしました.