I have actually found places where the ‘new’ keyword in the ‘confusing way’ actually does work out. But they’re VERY special use cases, and they’re also cases where… explaining the why it’s ok in that specific special situation is difficult if the person you’re explaining it to is novice.
It’s sort of like globals on crack. Globals are generally bad, but there’s cases where it’s good. You just need to know those cases. ‘new’ is like that, but its use cases are even slimmer.
I think a good example is something like this:
using System.Collections.Generic;
namespace com.spacepuppy.Collections
{
/// <summary>
/// Tests for equality based solely on if the references are equal. This is useful for UnityEngine.Objects that overrides the default Equals
/// operator returning false if it's been destroyed.
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectInstanceIDEqualityComparer<T> : EqualityComparer<T> where T : UnityEngine.Object
{
private static IEqualityComparer<T> _defaultComparer;
public new static IEqualityComparer<T> Default
{
get { return _defaultComparer ?? (_defaultComparer = new ObjectInstanceIDEqualityComparer<T>()); }
}
#region IEqualityComparer<T> Members
public override bool Equals(T x, T y)
{
return GetHashCode(x) == GetHashCode(y);
}
public override int GetHashCode(T obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
return obj.GetInstanceID();
}
#endregion
}
}
The class itself is an EqualityComparer for use in HashSet’s and Dictaionaries. It’s not really anything to do with why ‘new’ is useful here.
We’ll notice that the static ‘Default’ property is where I’ve used ‘new’.
This is because EqualityComparer already implements a static ‘Default’ property.
(of course, reading this… why isn’t is just implementing IEqualityComparer instead… I think there’s something I needed it to be an EqualityComparer explicitly, I can’t remember exactly. There was some weird place that this came up though… compatability with legacy junk)
BUT, because the usage of Defualt looks like this in either case:
var a = EqualityComparer<string>.Default
var b = ObjectInstanceIDEqualityComparer<Component>.Default
There’s none of that confusion going on. We’re forced to always be using the class’s explicit name anyways. You don’t expect both a and b to necessarily be the same thing.
Or another is like this:
internal class WeakReference<T> : WeakReference where T : class
{
public static WeakReference<T> Create(T target)
{
if (target == null) return WeakNullReference<T>.Singleton;
return new WeakReference<T>(target);
}
protected WeakReference(T target)
: base(target, false)
{
}
public new T Target
{
get { return base.Target as T; }
}
}
In this I create a WeakReference (because it doesn’t exist in the older version of mono in unity), and I want the ‘Target’ property to uncover the type as the generic type rather than as ‘object’.
Again, they both return the same object either way, just when we have it typed as WeakReference, we receive it cast as expected.
There is one other time that I use it as well, but it’s my more contentious use. I hate that it’s there, but I just can’t stand having it named anything else. And that is if I reimplement the now obsoleted component properties on a MonoBehaviour. Like ‘animation’ and ‘camera’. I have a small handful of components that explicitly deal with animation or cameras, and so having these properties make sense. And I couldn’t stand have a ‘Animation’ and ‘animation’ property one of which is obsolete. So I stuck a ‘new’ in… my heart isn’t so heavy about it because I know if you have it cast as MonoBehaviour, you expect it to be obsolete, it shouldn’t be there anyways. So when you have it as the explicit type where ‘new’ overrides, it should be there.
…
But these are just weird off cases where ‘new’ just happens to work. They’re few and far between, and usually the best judgement would be… use it if and only if you know its implications, and you know there’s no other way around using it.