polymorphic object "against the grain" of C# ?

B

Bill Woodruff

Consider a class that maintains two private instances of Generic Lists, one
of type string, one of type int :

class PolyClass
{
private List<string> listOStrings;
private List<int> listOInts;

public polyClass
{
listOStrings = new List<string>();
listOInts = new List<int>;
}
}

Now you define an Add operator that takes a string and an int as its
parameters, and an overload for the Add operator that takes an int and a
string as its parameters. You add a Get operator that takes an int as its
input parameter and returns the string in the listOStrings List at the same
index location as the int in the listOInts. You create an over-ride for the
Get operator that takes a sting as input parameter and returns the matching
int.

So now someone can use your class to add items using either the string-frst,
or the int-first flavours of Add. And use the Get operator similarly. Yes,
all of this compiles and works fine.

My question is : does this type of coding in a way "violate" the strongly
typed "basis" of C#; i.e., is it a "bad" practice, a "tolerable" but unwise
practice, or ?

thanks, Bill
 
P

Peter Bromberg [C# MVP]

This is kind of like the thread some time ago about whether its advisable to
have a method that returns an instance of Exception as its return value.
Generally, most said it wasn't good form.

I think the real question is what do you hope to accomplish with this, and
given that, is there a more elegant / usable way to do it?
-- Peter
Site: http://www.eggheadcafe.com
UnBlog: http://petesbloggerama.blogspot.com
Short Urls & more: http://ittyurl.net
 
I

Ignacio Machin ( .NET/ C# MVP )

Consider a class that maintains two private instances of Generic Lists, one
of type string, one of type int :

class PolyClass
{
    private List<string> listOStrings;
    private List<int> listOInts;

    public polyClass
    {
        listOStrings = new List<string>();
        listOInts = new List<int>;
    }

}

Now you define an Add operator that takes a string and an int as its
parameters, and an overload for the Add operator that takes an int and a
string as its parameters. You add a Get operator that takes an int as its
input parameter and returns the string in the listOStrings List at the same
index location as the int in the listOInts. You create an over-ride for the
Get operator that takes a sting as input parameter and returns the matching
int.

So now someone can use your class to add items using either the string-frst,
or the int-first flavours of Add. And use the Get operator similarly. Yes,
all of this compiles and works fine.

My question is : does this type of coding in a way "violate" the strongly
typed "basis" of C#; i.e., is it a "bad" practice, a "tolerable" but unwise
practice, or ?

thanks, Bill

Hi,

What happens if you put the same integer twice? with different strings
of course.

Anyway , I do not see how the described feature violate nothing in the
first place (except having two Add methods for no reason). Basically
you are implementing a collection of {int,string} that can be searched
by either component.
 
B

Ben Voigt [C++ MVP]

Bill said:
Consider a class that maintains two private instances of Generic
Lists, one of type string, one of type int :

class PolyClass
{
private List<string> listOStrings;
private List<int> listOInts;

public polyClass
{
listOStrings = new List<string>();
listOInts = new List<int>;
}
}

Now you define an Add operator that takes a string and an int as its
parameters, and an overload for the Add operator that takes an int
and a string as its parameters. You add a Get operator that takes an
int as its input parameter and returns the string in the listOStrings
List at the same index location as the int in the listOInts. You
create an over-ride for the Get operator that takes a sting as input
parameter and returns the matching int.

Sounds like a "content-addressable buffer". I'd probably prefer different
names for the functions instead of overloading, but the concept is valid.
 
B

Bill Woodruff

Hi, First, my apologies for not posting a more detailed original post, and
for posting code that had at least two spelling errros (my main development
machine is now non-nettable and non net-workable for unknown reasons, so I
hand typed the code on my laptop).

Appended a complete version of a class that demonstrates multi-type setting
and getting ... I have made the two lists here type specific but you could
just as well make them generic. In actual practice I use a "generic factory
object" built with generics to "spit out' classes whose types I specify like
this, but I made the included example non-generic to keep the code shorter
and focus on what for me is the main issue : "code usability and
appropriateness."

Real world use : synchronizing selection in one object/control where you
can only access items by indexing through ordinal position with selection in
another object/control where you can index by string ? Simulating something
in the real world that has bi-directional lookup of two types of data ?
Obviously constrained by the fact that lists cannot have duplicate keys.

My goal in presenting this is to ask the question if this type of class is
consistent with C#'s strongly typed nature. My own feelings about this are
ambiguous : on the one hand I feel it's a useful technique (particularly
when done up as a generic factory object); on the other hand I feel, in
terms of code maintenance, and programming style, it might be better not to
make it easy to set and get without explicitly stating the parameter type.
In other words having separately named 'getValueByKey and 'getKeyByValue
calls. Yes, in real world use there would be type-checking and throwing of
ArgumentException errors, but I've left that out for code brevity.

And, yes, there's no reason you can't make a class like this inherit from
List<T> or whatever and take advantage of the default behavior of that type
which, in this case, would require you to only implement one internal List,
and to override the Add operator of List once, etc.

Hope this is a clearer post. best, Bill

using System;
using System.Collections.Generic;
using System.Text;

namespace testPolyFunction
{
class polyClass
{
private List<string> listOStrings;
private List<int> listOInts;

public polyClass()
{
listOStrings = new List<string>();
listOInts = new List<int>();
}

public void set(string theString, int theInt)
{
listOStrings.Add(theString);
listOInts.Add(theInt);
}

public void set(int theInt, string theString)
{
set(theString, theInt);
}

public int get(string theString)
{
return listOInts[listOStrings.IndexOf(theString)];
}

public string get(int theInt)
{
return listOStrings[listOInts.IndexOf(theInt)];
}
}
}
 
B

Bob Hoeppner

I think an overloading of the Indexer and the Add methods is quirky, but
can be justifiable, especially if all coders involved understand the
possible downsides. The plus side is that there may be less code and
increased convenience for the person coding to the class. The downside
is that it is going to be harder for humans to understand, unless they
are familiar with it, and there may be a performance penalty depending
on the kind of indexer used.

here's an example:

public class Unit
{
//this class will hold the bool and double dictionaries of values
private Dictionary<int, double> mydoubles = new Dictionary<int,
double>();
private Dictionary<int, bool> mybools = new Dictionary<int, bool>();
#region Indexers
public bool this[int index]
{
get { return mybools[index]; }
set { mybools[index] = value; }
}
public double this[uint index]
{
get { return (mybools[(int)index]) ? 1d : 0d; }
set { mybools[(int)index] = (value == 1d) ? true : false; }
}
public double this[double index]
{
get { return mydoubles[(int)index]; }
set { mydoubles[(int)index] = value; }
}
#endregion
#region Add methods
public void Add(int index)
{
mybools.Add(index, false);
}
public void Add(uint index)
{
mybools.Add((int)index, false);
}
public void Add(double index)
{
mydoubles.Add((int)index, 0);
}
public void Add(int index, bool value)
{
mybools.Add(index, value);
}
public void Add(uint index, double value)
{
mybools.Add((int)index, ((value == 0)?false:true));
}
public void Add(double index, double value)
{
mydoubles.Add((int)index, value);
}
#endregion
public Unit Clone()
{
Unit temp = new PLCUnit();
foreach (KeyValuePair<int, bool> kvp in mybools)
temp.Add(kvp.Key, kvp.Value);
foreach (KeyValuePair<int, double> kvp in mydoubles)
temp.Add((double)kvp.Key, kvp.Value);
return temp;
}
}

//The overloading is especially helpful when setting bools from doubles
and vice versa
//especially if this happens a lot
myUnit[1234u] = dblvalue;
//is much easier on the coder than
myUnit[1234] = ((dblvalue == 0)? false : true);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top