2
\$\begingroup\$

I would like to greatly reduce verbosity of following code fragment.

What I'd like to do:

  1. Reduce number of occurrences of MaterialRangedParam<Value> and MaterialParam<Value>. C++ has typedef. C# doesn't. Is there keyword for own type of a class?
  2. Remove operator !=, operator ==, GetHashCode and Equals and make compiler generate them for me (the way C++ would do it).

How can I do that?

Additional info:

I'm already aware that clone method offers functionality similar to ICloneable. I have no need to support ICLoneable at the moment.

Usage pattern: Those items are going to be stuffed into several lists, arrays and hashtables, compared to each other, assign is going to be called often.

Advice?

public class MaterialParam<Value> where Value: System.IEquatable<Value>{
    public string name = "";
    public string shaderValueName = "";
    public Value value;// = Value();
    public void assignFrom(MaterialParam<Value> other){
        name = other.name;
        shaderValueName = other.shaderValueName;
        value = other.value;
    }

    public MaterialParam<Value> clone(){
        var result = new MaterialParam<Value>();
        result.assignFrom(this);
        return result;
    }

    public override bool Equals(object obj){
        if(!(obj is MaterialParam<Value>))
            return false;

        MaterialParam<Value> other = (MaterialParam<Value>)obj;
        if((System.Object)other == null){
            return false;
        }           

        return (this.name == other.name) && 
            (this.shaderValueName == other.shaderValueName) &&
                (this.value.Equals(other.value));
    }

    public override int GetHashCode(){
        return name.GetHashCode() ^ shaderValueName.GetHashCode() 
            ^ value.GetHashCode();
    }

    public static bool operator !=(MaterialParam<Value> a, MaterialParam<Value> b){
        return !(a == b);
    }

    public static bool operator ==(MaterialParam<Value> a, MaterialParam<Value> b){
        return a.Equals(b);
    }
};

public class MaterialRangedParam<Value>: MaterialParam<Value> where Value: System.IEquatable<Value>{
    public Value minValue;// = Value();
    public Value maxValue;// = Value();
    public void assignFrom(MaterialRangedParam<Value> other){
        base.assignFrom(other);
        minValue = other.minValue;
        maxValue = other.maxValue;
    }

    new public MaterialRangedParam<Value> clone(){
        var result = new MaterialRangedParam<Value>();
        result.assignFrom(this);
        return result;
    }

    public override bool Equals(object obj){
        if (!base.Equals(obj))
            return false;

        if(!(obj is MaterialRangedParam<Value>))
            return false;

        MaterialRangedParam<Value> other = (MaterialRangedParam<Value>)obj;
        if((System.Object)other == null){
            return false;
        }           

        return (minValue.Equals(other.minValue)) && (maxValue.Equals(other.maxValue));
    }

    public override int GetHashCode(){
        return base.GetHashCode() ^ minValue.GetHashCode() ^ maxValue.GetHashCode();
    }

    public static bool operator !=(MaterialRangedParam<Value> a, MaterialRangedParam<Value> b){
        return !(a == b);
    }

    public static bool operator ==(MaterialRangedParam<Value> a, MaterialRangedParam<Value> b){
        return a.Equals(b);
    }
}
\$\endgroup\$
2
\$\begingroup\$

This code looks very Java-like in its style and implementation. Maybe C#1.0 at its best. There are a number of stylistic and idiomatic changes I'd make to make these two classes "play well" in the C# / .NET world of 2015. Without going into exhaustive detail, I employed object immutability, parameterized constructors, implementing IClonaeble, utilizing properties, correctly implementing Equals and ==.

With regards to the two things that you'd like to do, sorry, neither are possible (nor really desirable) with C# for various reasons that you can find on MSDN.

The code:

public class MaterialParam<TValue> where TValue : IEquatable<TValue>, ICloneable
{
    public readonly string name = string.Empty;

    public readonly string shaderValueName = string.Empty;

    public readonly TValue value; // = Value();

    public MaterialParam()
    {
    }

    public MaterialParam(string name, string shaderValueName, TValue value)
    {
        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        if (shaderValueName == null)
        {
            throw new ArgumentNullException("shaderValueName");
        }

        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        this.name = name;
        this.shaderValueName = shaderValueName;
        this.value = value;
    }

    public MaterialParam(MaterialParam<TValue> other)
    {
        if (other == null)
        {
            throw new ArgumentNullException("other");
        }

        this.name = other.name;
        this.shaderValueName = other.shaderValueName;
        this.value = other.value;
    }

    public string Name
    {
        get
        {
            return this.name;
        }
    }

    public string ShaderValueName
    {
        get
        {
            return this.shaderValueName;
        }
    }

    public TValue Value
    {
        get
        {
            return this.value;
        }
    }

    public static bool operator ==(MaterialParam<TValue> a, MaterialParam<TValue> b)
    {
        if (ReferenceEquals(a, b))
        {
            return true;
        }

        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }

        return a.Equals(b);
    }

    public static bool operator !=(MaterialParam<TValue> a, MaterialParam<TValue> b)
    {
        return !(a == b);
    }

    public MaterialParam<TValue> Clone()
    {
       return new MaterialParam<TValue>(this);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is MaterialParam<TValue>))
        {
            return false;
        }

        var other = (MaterialParam<TValue>)obj;

        return (this.name == other.Name)
            && (this.shaderValueName == other.ShaderValueName)
            && this.value.Equals(other.Value);
    }

    public override int GetHashCode()
    {
        return this.name.GetHashCode()
            ^ this.shaderValueName.GetHashCode()
            ^ this.value.GetHashCode();
    }
}

public class MaterialRangedParam<TValue> : MaterialParam<TValue> where TValue : IEquatable<TValue>, ICloneable
{
    public readonly TValue minValue; // = Value();

    public readonly TValue maxValue; // = Value();

    public MaterialRangedParam(TValue minValue, TValue maxValue)
    {
        if (minValue == null)
        {
            throw new ArgumentNullException("minValue");
        }

        if (maxValue == null)
        {
            throw new ArgumentNullException("maxValue");
        }

        this.minValue = minValue;
        this.maxValue = maxValue;
    }

    public MaterialRangedParam(MaterialRangedParam<TValue> other) : base(other)
    {
        if (other == null)
        {
            throw new ArgumentNullException("other");
        }

        this.minValue = other.MinValue;
        this.maxValue = other.MaxValue;
    }

    public TValue MinValue
    {
        get
        {
            return this.minValue;
        }
    }

    public TValue MaxValue
    {
        get
        {
            return this.maxValue;
        }
    }

    public static bool operator ==(MaterialRangedParam<TValue> a, MaterialRangedParam<TValue> b)
    {
        if (ReferenceEquals(a, b))
        {
            return true;
        }

        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }

        return a.Equals(b);
    }

    public static bool operator !=(MaterialRangedParam<TValue> a, MaterialRangedParam<TValue> b)
    {
        return !(a == b);
    }

    public new MaterialRangedParam<TValue> Clone()
    {
        return new MaterialRangedParam<TValue>(this);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is MaterialRangedParam<TValue>))
        {
            return false;
        }

        var other = (MaterialRangedParam<TValue>)obj;

        return base.Equals(other) && this.minValue.Equals(other.MinValue) && this.maxValue.Equals(other.MaxValue);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode() ^ this.minValue.GetHashCode() ^ this.maxValue.GetHashCode();
    }
}
\$\endgroup\$
  • \$\begingroup\$ Thank you for the response. "you can find on MSDN" Would be nice to have keywords/phrase to search for as a starting point. The reason why it looks like Java/C# 1.0 is because I come from C++ background and picked C# 2 weeks ago for unity project. The reason why I wanted to shorten the code is because those two classes are describing incredibly low-level structure that is used in 2..5 subroutines max and because it takes 5 times less lines to describe the concept in C++ (because C++ provides default opeartors). They technically should've been structs but I wanted constructor. \$\endgroup\$ – SigTerm Apr 13 '15 at 19:52
  • \$\begingroup\$ Also, I've noticed that code written in "Equals" follows pretty much the same pattern (compare object to null, attempt cast, then "equal" fields one by one), so it is surprising that there is no automatic way to request Equals for your class. Oh well. \$\endgroup\$ – SigTerm Apr 13 '15 at 19:54
  • \$\begingroup\$ Color me wrong on one point. You can have typedef-like behavior with the using statement at the top of your files, such as using Bob=MaterialRangedParam<double>;. \$\endgroup\$ – Jesse C. Slicer Apr 13 '15 at 20:12
  • \$\begingroup\$ It is a pity you haven't provided keywords for explanation that "I can find on MSDN", but your code example made me aware of idea of using immutable objects in C#. Marking your answer as accepted. Good day. \$\endgroup\$ – SigTerm Apr 14 '15 at 11:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.