Steffen
Steffen

Reputation:

C# mimic overridding the assignment operator (=)

I've got a bit of a problem with a somewhat simple wrapper class I have.

It looks something like this:

public class Wrapper<T>
{
  private T _value;

  public Wrapper<T>(T value)
  {
    _value = value;
  }

  public static implicit operator Wrapper<T>(T value)
  {
    return new Wrapper<T>(value);
  }

  public static implicit operator T(Wrapper<T> value)
  {
    return value._value;
  }
}

I've overriden the implicit converters from and to T, so it behaves almost like an instance of T itself.

e.g.

Wrapper<int> foo = 42;

However I've got a slight problem when assigning one instance of Wrapper to another, since I only want to assign the value of the second Wrapper class.

So right now, I have to do this:

Wrapper<int> foo = 42;
Wrapper<int> bar = (int)foo;

Or expose _value publicly through a property.

However since this is in a library, and I don't want the user to depend on remembering this, do you guys have any idea how I could mimic overridding the assignment operator ?

The problem in just changing the pointer (as it does when assigning a class instance to another), is that I've got a dictionary of pointers to these Wrapper objects, so I cannot have them changing all the time, since the dictionary would stop matching then.

I can see if this is somewhat confusing, so if I've left anything important out, please feel free to ask :-)

Upvotes: 6

Views: 11459

Answers (8)

C Perkins
C Perkins

Reputation: 3881

This old post stills needs additional information to be complete. It's apparent that the original desired behavior cannot be accomplished since the = operator cannot be overloaded, and likewise C# cannot be "tricked" into casting an object to its own type... it will always boil down to a class reference assignment. But Steffen's further posts show the Wrapper class being used not just with local variables, but as a class field type. The desired semantics can be used AND the integrity of the internal dictionary maintained by using class properties instead of public fields.


Even keeping the original given Wrapper<T> class with both its implicit operators, here's code that would work:

public class CustomerTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

public class UserTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

If this change were made, it would not break existing code since it still allows various modes of setting the property:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();

user.Name = customer.Name; //*** No longer breaks internal data structures

user.Name = "string literal";  // Works as expected with implicit cast operator
user.Name = (string)customer.Name; // Still allowed with explicit/implicit cast operator
user.Name = customer.Name.Value; // Also works if Value property is still defined

Because this still doesn't answer the original question, use of the Wrapper class could still be problematic if its used outside the class property context, i.e. passed between object, etc. Perhaps the entire Wrapper class could be eliminated with the proper class design, including use of property set/get accessors.

Upvotes: 0

Steffen
Steffen

Reputation:

A J Lane I see what you mean, and I guess you're right - I just wanted to make it as simple as possible to use the library.

The reason for the implicit cast from DbValue to T, is to simply functions which expects T.

for example

literalSomething.Text = Server.HtmlEncode(SomeTable.SomeStringColumn);

rather than

literalSomething.Text = Server.HtmlEncode((string)SomeTable.SomeStringColumn);

This requires the cast to be implicit.

That being said I just read your comment whilst typing this, and I can see that's quite the issue.

I think I'll go back to exposing value through a property, it just requires the developer to type more, and kinda makes the code ugly I think.

Just imagine DbValue:

if (someValue.Value.HasValue) // someValue is DbValue<int?>

But then again it's probably better with "ugly" code, than code which behaves differently from what you'd expect by merely reading it.

I guess this question ends up as a "best practice" question really.

So to conclude, I'll create a Value property and use that instead of implicit casts, and the developer using the library will just have to live with that.

Thanks for your inputs :-)

Upvotes: 0

ajlane
ajlane

Reputation: 1959

Don't implicitly cast your wrapper both ways.

public class DBValue<T>
{
    public static implicit operator DBValue <T>(T value)
    {
         return new DBValue<T>(value);
    }

    public static explicit operator T(DBValue <T> dbValue)
    {
         return dbValue.Value;
    }

    private readonly T _value;
    public T Value { get { this._value; } }

    public DBValue(T value)
    {
         this._value = value;
    }
}

Casting from DBValue<T> to T is a lossy conversion (as a minimum, you lose the fact that it's a value from the database), and by best-practice should be explicit. If you don't lose anything by casting from DBValue<T> to T, you might as well just use properties that return T.

Basically, you've already seen why you shouldn't be trying to do this: if a DBValue can be substituted for T and the other way around, how does the compiler (or developer) know which one to choose?

Requiring down-stream developers to write:

string value = MyProperty.Value

or

string value = (string)MyProperty

instead of

string value = MyProperty

...isn't all that onerous, and makes sure that everyone knows exactly what's going on.

EDIT:

To actually answer the question, you can't override reference assignment - or make it look like you have - but you shouldn't really need to.

Upvotes: 1

Steffen
Steffen

Reputation:

I just looked into it, making the class a struct is really not an option, since it has some logic in the parameterless constructor, plus it inherits an abstract class, which contains internal abstract functions.

I cannot use an interface, as that'd make those functions public, which would break the logic entirely.

I can post the entire class if that'd be helpful, but it's somewhat long (130 lines) Or I could toss up on a seperate server, if that'd be better ? (though it hurts the integrity of this question, as I may delete it eventually from that server)

Also explaining the class is really difficult, without writing a complete essay :-/

Anyway I'll try to illustrate the problem I'm having.

Assume 2 table classes: CustomerTable and UserTable:

public class CustomerTable
{
  Wrapper<string> Name;
}

public class UserTable
{
  Wrapper<string> Name;
}

Now the problem is that some other developer, may use the above code as follows:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();
user.Name = customer.Name; // This breaks my internal dictionary

What the developer should had done, in order for it to work, was:

user.Name = (string)customer.Name;

The problem is however, who in their right mind would think about that, when writing code ?

Even if I used a Value property, the developer would still have to remember to write

user.Name = customer.Name.Value; // or user.Name.Value = ....

And again the developer may forget this, and all of a sudden he gets exceptions, or worse: data which isn't persisted to the database.

So my issue is really, that I want the wrapper to be completely transparent (it should be usable as if it was in fact the class/primitive it's wrapping). However when assigning from one wrapper to another, my internal logic breaks.

Phew a lot of writing, and a lot of code - let me know if I overdo the writing.

Upvotes: 0

Daniel Earwicker
Daniel Earwicker

Reputation: 116764

This is what properties are for. They allow you to define what assignment means. You can't define it for a class or struct itself because they are already defined by the language to do necessary things. Just add a Value property to the class.

Alternatively, edit your question to give a broader description of your design and how this Wrapper fits into it, as someone may be able to suggest a simpler approach.

Upvotes: 0

Jim Mischel
Jim Mischel

Reputation: 134125

Since the assignment operator can't be overloaded, there isn't a real good solution. As somebody else pointed out, using a struct will give you the assignment semantics that you want, but then you're faced with value semantics--often not a good thing.

One option is to overload the constructor:

public Wrapper(Wrapper<T> w)
{
    _value = w._value;
}

Which would result in this syntax:

Wrapper<int> foo = 42;
Wrapper<int> bar = new Wrapper<int>(foo);

Although more verbose than what you have, it reads better.

Or you could add a Clone method (not the ICloneable interface), so that you could write:

Wrapper<int> bar = foo.Clone();

You could get really creative and overload some operator, making it do essentially nothing. I wouldn't recommend that, though. Using operator overloading for those kinds of things typically makes code cryptic and often breaks.

Upvotes: 5

zildjohn01
zildjohn01

Reputation: 11515

You could make Wrapper<T> a struct. However I'm not sure if this would suit your application design or not.

Upvotes: 3

FlySwat
FlySwat

Reputation: 175733

If you look at Nullable<T>...which does a very similar thing to what you are doing here, it exposes the internal value using a .Value property.

The problem in just changing the pointer (as it does when assigning a class instance to another), is that I've got a dictionary of pointers to these Wrapper objects, so I cannot have them changing all the time, since the dictionary would stop matching then.

I'm not sure I follow this, what exactly are you storing in the dictionary? Because if you are storing references, the CLR will update them as necessary.

Upvotes: 1

Related Questions