Using System.Linq automatically without using directive?

So this is a strange question. I’m a designer and not a career programmer, so a lot of my code knowledge has been based on exploration of Unity itself. I’ve often used a public array of strings in a trigger script, then used a simple line like this to check if the trigger behavior should execute:

    public String[] validTags;
   
    void OnTriggerEnter(Collider other)
    {
        if (validTags.Contains (other.collider.tag))
        {
            //behavior goes here
        }
    }

To my knowledge this was built-in behavior for UnityEngine or System.Collections. However, I grabbed an update to an asset on the asset store and had to delete it, only after I did I found that all of my script files that used the above snippet were returning an error:

Type string[ ]' does not contain a definition for Contains’ and no extension method Contains' of type string[ ]’ could be found (are you missing a using directive or an assembly reference?)

After this happened, I found out I should actually be using System.Linq to get this functionality. So why didn’t I have to type this in before? I’ve been working on this particular project for months and non of the other developers had this sudden issue; all the code still works without using System.Linq, its only on my machine that it’s required. I don’t want to commit to the depot until I understand what has happened or what the proper practice should be. Can anyone explain what I’m missing here? Thanks.

To use the language integrated query extension methods you do need to include them (add a using statement), that is the correct thing to do.

The asset may have had a static class containing it’s own extensions to the IEnumerable derivations to implement a Contains method.

You can implement your own ones quite easily into the global scope (no namespace)…
Here are a few examples:

#region usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Diagnostics; 
#endregion

public static class Extensions
{
  private static Random rnd = new Random();

  #region <Templated>

  /// <summary>
  /// Finds the number of Differences between this and obj  
  /// {1, 2, 3, 4, 5} and {1, 5, 3, 4, 2} would have a Distance of 2
  /// </summary>
  /// <param name="value"></param>
  /// <param name="second">the object to compare to</param>
  /// <returns>Number of Differences</returns>
  public static int Distance<TSource>(this TSource[] value, TSource[] second)
    where TSource : IEquatable<TSource>
  {
    int diff = 0;
    for (int i = 0; i < second.Length; i++)
    {
      if (!value[i].Equals(second[i]))
        diff++;
    }

    return diff;
  }


  #region [].Copy()

  /// <summary>
  /// Returns a new copy of this array
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <param name="source"></param>
  /// <returns></returns>
  public static TSource[] Copy<TSource>(this TSource[] source)
  {
    var arr = new TSource[source.Length];
    source.CopyTo(arr, 0);
    return arr;
  }

  /// <summary>
  /// Copies n items from this array into a new one; from start for n 
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <param name="source"></param>
  /// <param name="start"></param>
  /// <param name="n"></param>
  /// <returns></returns>
  public static TSource[] Copy<TSource>(this TSource[] source, int start, int n)
  {
    var arr = new TSource[n];

    uint ind = 0;
    for (int i = start; i < start + n; i++)
    {
      arr[ind] = source[i];
      ind++;
    }
    return arr;
  }

  #endregion

  #region ICollection.Add()

  /// <summary>
  /// Adds a collection of items to this collection
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <typeparam name="S"></typeparam>
  /// <param name="list"></param>
  /// <param name="values"></param>
  public static void AddRange<TSource, S>(this ICollection<TSource> list, params S[] values)
    where S : TSource
  {
    foreach (S value in values)
      list.Add(value);
  }

  /// <summary>
  /// Adds an item to the collection, but only if it's not already present
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <typeparam name="S"></typeparam>
  /// <param name="list"></param>
  /// <param name="value"></param>
  public static void AddIfUnique<TSource, S>(this ICollection<TSource> list, S value)
    where S : TSource
  {
    if (!list.Contains(value))
      list.Add(value);
  }

  /// <summary>
  /// Adds a set of items to the collection, but only if an item isn't already present
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <typeparam name="S"></typeparam>
  /// <param name="list"></param>
  /// <param name="values"></param>
  public static void AddIfUnique<TSource, S>(this ICollection<TSource> list, params S[] values)
    where S : TSource
  {
    foreach (S value in values)
      if (!list.Contains(value))
        list.Add(value);
  }

  /// <summary>
  /// Shuffles a set by seed value
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <param name="list"></param>
  /// <param name="seed"></param>
  /// <returns></returns>
  public static IEnumerable Shuffle<TSource>(this IEnumerable<TSource> list, int seed)
  {
    Random rnd = new Random(seed);

    return list.OrderBy(p => rnd.Next(100)); ;
  }

  /// <summary>
  /// Shuffles a set randomly
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <param name="list"></param>
  /// <returns></returns>
  public static IEnumerable<TSource> Shuffle<TSource>(this IEnumerable<TSource> list)
  {
    return list.OrderBy(p => rnd.Next(100)); ;
  }



  #endregion

  #region IEnumerable.ForEach()

  /// <summary>
  /// 
  /// </summary>
  /// <typeparam name="TSource"></typeparam>
  /// <param name="enum"></param>
  /// <param name="mapFunction"></param>
  public static void ForEach<TSource>(this IEnumerable<TSource> @enum, Action<TSource> mapFunction)
  {
    foreach (var item in @enum) mapFunction(item);
  }

  #endregion

  public static T To<T>(this IConvertible obj)
  {
    return (T)Convert.ChangeType(obj, typeof(T));
  }

  [DebuggerStepThrough()]
  public static bool Is<T>(this object item) where T : class
  {
    return item is T;
  }

  [DebuggerStepThrough()]
  public static bool IsNot<T>(this object item) where T : class
  {
    return !(item.Is<T>());
  }

  [DebuggerStepThrough()]
  public static T As<T>(this object item) where T : class
  {
    return item as T;
  }



  #endregion

}

‘Contains’ has never been a part of T[ ] arrays
It IS, however, a part of List generic lists.
You sure you weren’t using generic list before?

You could modify the template file new scripts are based off of to auto include the ‘using System.Linq;’ line in it.

That’s how the ‘using UnityEngine;’ line is auto filled out for you.

Here’s a link describing how to edit the template.