Comparing generic value/string types

A

Andrus

I need to create generic table field level cache.
Table primary key (PrimaryKeyStructType) can be int, string or struct
containing int and string fields.
FieldName contains table field name to be cached.

Remove() should remove table row from cache. Row is identified by table
primary key.
Trying compile class causes error

Operator '==' cannot be applied to operands of type

'PrimaryKeyStructType' and 'PrimaryKeyStructType'

How to fix this ?

Andrus.



using System.Collections.Generic;

class Cache<PrimaryKeyStructType> {
struct Key {
public PrimaryKeyStructType PrimaryKey1Value;
public string FieldName;
}

Dictionary<Key, object> Store = new Dictionary<Key, object>();

/// <summary>
/// Removes table row from cache by removing all fields for this row
/// </summary>
/// <param name="id">Primary key value for row to be removed</param>
void Remove(PrimaryKeyStructType id) {
foreach (Key k in Store.Keys)
// Error 1 Operator '==' cannot be applied to operands of type
// 'PrimaryKeyStructType' and 'PrimaryKeyStructType'
if (k.PrimaryKey1Value == id)
Store.Remove(k);
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Andrus,

In this case, you will have to create an equality (==) operator for the
type. In addition to doing that, you are going to want to consider
overriding the Equals and GetHashCode methods on the type as well.
 
A

Andrus

Nicholas,

this is generic type.
Ho to create equality operator and override Equals and GetHashCode for
generic type ?
This can be done only for convrete class.

Andrus.
 
J

Jon Skeet [C# MVP]

this is generic type.
Ho to create equality operator and override Equals and GetHashCode for
generic type ?
This can be done only for convrete class.

No, you can override both of these in generic types, just as normal.

The easiest way to implement them is to use
EqualityComparer<T>.Default for each field (replacing the T
appropriately) to take IComparable etc into account.

Jon
 
A

Andrus

this is generic type.
No, you can override both of these in generic types, just as normal.

The easiest way to implement them is to use
EqualityComparer<T>.Default for each field (replacing the T
appropriately) to take IComparable etc into account.

Jon,

thank you.

If generic type can be int or string, how to implement this ?
Shound I use GetTypeCode() to determine is it int or string and then
cast variable to int or string to make comparison ?

Where to find example about this ?

Andrus.
 
J

Jon Skeet [C# MVP]

If generic type can be int or string, how to implement this ?
Shound I use GetTypeCode() to determine is it int or string and then
cast variable to int or string to make comparison ?

No, there's no need. EqualityComparer<T>.Default will do it all for
you. If you're in a generic type with type parameter of TFoo, just use
EqualityComparer said:
Where to find example about this ?

If you could give a short but *complete* program, we could probably
implement it for you in a few lines.

Jon
 
A

Andrus

If you could give a short but *complete* program, we could probably
implement it for you in a few lines.

Jon,

Thank you.
Here is complete program.

Andrus.


using System.Collections.Generic;
using System;

namespace Business {
class Application {

static Cache<int, Customer> CustomerCache = new Cache<int, Customer>();
static Cache<string, Order> OrderCache = new Cache<string, Order>();

static void Main() {
// Add some column values to cache
// Real values are retrieved from database in application.
CustomerCache.Add("Name", 1, "Jon");
CustomerCache.Add("BirthYear", 1, 1963);
CustomerCache.Add("Name", 2, "Martin");
OrderCache.Add("Description", "PO12", "This is order 12");

// Remove all Customer 1 data from cache
// Should remove both Name and BirthYear for id 1
CustomerCache.Remove(1);

}
}

// Bussiness object ypesd corresponding to database tables
class Customer {
}

class Order {
}


}

/// <typeparam name="PrimaryKeyStructType">type of object primary
key</typeparam>
/// <typeparam name="ObjType">object whose field value is cached.
/// Currently used for debugging only.
/// Reserved for future use when multi-object type cache is implemented.
/// </typeparam>
class Cache<PrimaryKeyStructType, ObjType> {

struct Key {
public PrimaryKeyStructType PrimaryKey1Value;
public string FieldName;
Type obj;

public Key(PrimaryKeyStructType primaryKey1Value, string fieldName) {
PrimaryKey1Value = primaryKey1Value;
FieldName = fieldName;
obj = typeof(ObjType);
}
}

Dictionary<Key, object> Store = new Dictionary<Key, object>();

/// <summary>
/// Removes table row from cache by removing all fields for
/// this row
/// </summary>
/// <param name="id">Primary key value for row to be removed</param>

internal void Remove(PrimaryKeyStructType id) {

foreach (Key k in Store.Keys)
if (k.PrimaryKey1Value == id)
Store.Remove(k);
}

/// <summary>
/// Adds database table column value to cache
/// </summary>
/// <param name="fieldExpr">object </param>
/// <param name="id"></param>
/// <param name="fieldValue"></param>
internal void Add(string fieldExpr, PrimaryKeyStructType id, object
fieldValue) {

Key key = new Key(id, fieldExpr);
Store.Add(key, fieldValue);
}


}
 
J

Jon Skeet [C# MVP]

Andrus said:
Here is complete program

Okay, a few problems:

1) Use the "T" prefix for type parameters - it makes it clearer what's
going on.

2) You shouldn't do this:

foreach (Key k in Store.Keys)
if (k.PrimaryKey1Value == id)
Store.Remove(k);

as it will blow up when it comes back to iterating after removing a
key.

You *could* do:

foreach (Key k in Store.Keys)
{
if (k.PrimaryKey1Value.Equals(id))
{
Store.Remove(k);
break;
}
}

but that's not ideal and may not work depending on what you're
expecting it to do. The trouble is it's not clear what your key is
really meant to be. Should you be able to have multiple entries for the
same PrimaryStructType?
 
A

Andrus

1) Use the "T" prefix for type parameters - it makes it clearer what's
going on.

Thank you.
I renamed type parameters by adding T prefix.
2) You shouldn't do this:

foreach (Key k in Store.Keys)
if (k.PrimaryKey1Value == id)
Store.Remove(k);

as it will blow up when it comes back to iterating after removing a
key.

You *could* do:

foreach (Key k in Store.Keys)
{
if (k.PrimaryKey1Value.Equals(id))
{
Store.Remove(k);
break;
}
}

but that's not ideal and may not work depending on what you're
expecting it to do. The trouble is it's not clear what your key is
really meant to be. Should you be able to have multiple entries for the
same PrimaryStructType?

Yes.
PrimaryStructType is Customer id.
Cache may contain multiple items with same customer id: every customer table
field: customer name, address etc
is stored as separate cache entity.

Caching each property separately avoid retrieving whole row from database.
I retrie only property which I need in report.

I'm not sure is it reasonable to cache every property separatery.
However I expect that this decreases amout of data mover over internet
connection and increases application speed.
Maybe I should use whole row cache and retrieve whole table row when I need
first property of this row.

Andrus.
 
J

Jon Skeet [C# MVP]

PrimaryStructType is Customer id.
Cache may contain multiple items with same customer id: every customer table
field: customer name, address etc
is stored as separate cache entity.

Caching each property separately avoid retrieving whole row from database.
I retrie only property which I need in report.

I'm not sure is it reasonable to cache every property separatery.
However I expect that this decreases amout of data mover over internet
connection and increases application speed.
Maybe I should use whole row cache and retrieve whole table row when I need
first property of this row.

Yes, it would not only be simpler to cache the whole row, but I suspect
it would also be faster. The simple part is the more important bit
usually though...
 

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