Im looking to convert some existing code to use Generics.
currently the code looks something like this:
public abstract class DataLayerBase{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
public virtual Object Get(Object PrimaryKey);
//Returns an object which depends on the Type the derived class handles
// the parameter is the value of the PrimaryKey, it's type is dependent on the type
// that the derived class handles.
public virtual void Save(Object o);
public virtual void Delete(Object o);
}
generally in my derived classes i will have members along the lines of
public new ExampleObject Get(int ID); // ID is the primary key in the Table, it's of type Int32
public new void Save (ExampleObject o);
public new void Delete(ExampleObject o);
currently, im rewriting the class as such:
public abstract class DataLayerBase<T>{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
public virtual T Get(Object PrimaryKey);
public virtual void Save(T inst);
public virtual void Delete(T inst);
}
My questions are...
1) can i declare get such that it returns T, and takes a parameter who's type is the same as a particular field of the type the datalayer will be returning?
2) how do i go about declaring the derived class to handle a particular Type?
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
I understand the concept of using the where to constrain T,
what a really want to say is something like...
public abstract class DataLayerBase<T> where T : IDataObject{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
// Assuming IDataObject declares PrimaryKeyField, Im Fairly sure this will not work
public virtual T Get(typeof(T.PrimaryKeyField) PrimaryKey);
public virtual void Save(T inst);
public virtual void Delete(T inst);
}
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
What abstracting the relationship OUTSIDE the class in a class or an interface:
public abstract class DataLayerBase<T, Tkey> where T : IDataObject,
TKey: IYourDataKey<T> {
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
public class HighSchool
{
// private members
int _iID;
string _strPrinciple;
string _strName;
// public accessors
public int ID
{
get { return _iID;}
set { _iID = value; }
}
public string Principle
{
get { return _strPrinciple;}
set { _strPrinciple = value; }
}
public string Name
{
get { return _strName;}
set { _strName = value; }
}
}
(lets forget the where constraints for a sec...)
My understanding is that i could declare a class:
public class HighSchoolDataLayer : BaseDataLayer<HighSchool,System.Int32>{
public HighSchoolDataLayer (string ConnectionString):base(ConnectionString){
}
public override HighSchool Get (Int32 PrimaryKey)...
public override void Save(HighSchool inst) ...
public override void Delete(HighSchool inst) ...
}
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
1. You are hardwiring the type int and defeating the purpose of a generic key type.(which inscidentally may be a good thing. SQL uses very few types and can accomplish a lot.)
2. In general, the most important part of generics - performance of collections of known types is not at play here. You don't have a collection. What is wrong with 'traditional' inheritance and both base and derived types have the same type for key?
See, these classes are comming out of a code generator. Each Class the Generator creates Gets a corresponding class Derived from DataLayerBase. For a spesific class, the type of the primary key
should be hard-wired. However, Diffrent classes may have diffrent Primary Keys with diffrent types.
In this case generics are being used because they allow me to declare DataLayerBase which doesnt care about Types and declare derived classes which only handle a single type.
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
I take it IDataObject is not the one from Windows.Forms?
JeffreyABecker
For a spesific class, the type of the primary key should be hard-wired.
That is exactly what this does
public abstract class DataLayerBase<T, Tkey> :IKeyable<T, Tkey>
where T : IDataObject
Tkey may be defined in the base class itself but probably better in an interface.
public abstract class DataLayerBase<T, Tkey> : IBusinessObject <T, Tkey>
The relationship still needs to be defined.
public T GetByKey(Tkey key);
You still ought to check in your db if you really have to have type safety of the key. It sure would save you a lot of complexity in the inheritance tree if you don't.
public abstract class DataAccessLayerBase{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
Derived DataAccessLayers then Explicitly Implement the Interface and Inherit the Base class.
as such:
public class HighschoolDataLayer: IDataAccessLayer, DataAccessLayerBase{
public HighschoolDataLayer(string ConnectionString): base(ConnectionString){
}
Highschool Get(int ID){
// ...
}
public void Save(HighSchool s){
//...
}
public void Delete(HighSchool s){
//...
}
// This is the explicit interface implementation. these methods will only be accessable
// if this object has been cast into an IDataAccessLayer. We still need to call the
// Real versions in case someone does cast into an IDataAccessLayer
Object IDataAccessLayer.Get(Object value){
if(value is Int32)
return this.Get((Int32)value);
}
void IDataAccessLayer.Save(Object value){
if(value is Highschool)
return this.Save((Highschool)value);
}
void IDataAccessLayer.Delete(Object value){
if(value is Highschool)
return this.Delete((Highschool)value);
}
}
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
I am not a performance pro. The way you can tell the real perf pros is they never talk off the top of their heads. When you ask them a question they are already setting up all kinds of bench tests...
However I doubt if any collection of HighSchools(or their derivatives) would ever be represented by
int, so casting would never mean unnecessary boxing and unboxing.
The only place you might is the key. Increasingly, I see fewer and fewer integer data type @@Identity columns. The Yukon samples are all Guids.
I guess that facilitates with merging conflicts and because of cheap storage. So chances are even there you wouldn't be penalized.
Then leave generics where they shine - in collections.
GUIDs are nice because they are (theoreticly) unique across the whole database and not User guessable. I wish other databases had support for something similar. With postgreSql & Firebird, I could probably hack something together by hashing the mac address
& current time. I have no idea how i'd do this in oracle or mysql though.
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Generics rock and I believe they shine in more places than just in collections. I was a little disappointed to see that you dropped generics all together! [;)]
In addition, do you know about the new TableAdapters in 2.0....HOLY YES! They rock as well. Anyway, I wanted to throw my two cents out there. This is one way you could implement your BOM:
public class HighSchool {
int iD;
string principle;
string name;
public int ID {
get { return this.iD; }
set { this.iD = value; }
}
public string Principle {
get { return this.principle; }
set { this.principle = value; }
}
public string Name {
get { return this.name; }
set { this.name = value; }
}
}
public interface IDataAccessLayer<T, K> {
T Get(K primaryKey);
void Save(T item);
void Delete(T item);
}
public abstract class DataLayerBase {
private string connectionString;
public DataLayerBase(string connectionString) {
this.connectionString = connectionString;
}
public class HighSchoolDataLayer : DataLayerBase, IDataAccessLayer<HighSchool, int> {
public HighSchoolDataLayer(string connectionString) : base(connectionString) { }
public HighSchool Get(int primaryKey) {
throw new NotImplementedException();
}
public void Save(HighSchool item) {
throw new NotImplementedException();
}
public void Delete(HighSchool item) {
throw new NotImplementedException();
}
}
In addition, you will notice that I was able to implement the interface implicitly which does not require the additional interface methods implementaion.
Terrific snippet. I am still not sure what you mean by
nicequy
...implement the interface implicitly which does not require the additional interface methods implementaion.
1.What additional methods? There are only three and all accounted for.
2.What do you mean by implicit? That you didn't explicitly specify the interface members in which case you could have picked to inherit some and not all of the interface members like:
public class HighSchoolDataLayer : DataLayerBase {
public HighSchoolDataLayer(string connectionString) : base(connectionString) { }
public HighSchool IDataAccessLayer<HighSchool, int>.Get(int primaryKey) {
throw new NotImplementedException();
}
}
3. What is he accomplishing over non-generics besides some type rigor (not type safety) because now it is an interface he is inheriting from and implementing the the method bodies de novo?
This itself is a lot if the code is intended for distribution. However I believe he was looking to bake this logic into the base class and somehow have it inherited in a smart type safe way down the line, and that is not accomplished here as he needs to implement
it in every descendant.
Implementing it in every descendant really isnt all that much of a hastle. Remember that this code is getting generated (mostly) by a templating system anyway. The bigger issue is that im not really sure I want to be going with generics. Currently the system
runs on Mono 1.9.2. All the CLR & C# 2.0 features are there, but the ASP.Net 2.0 and 2.0 Framework libraries are far from complete. Since the templates are based on CodeDOM, I'd have to switch to a 2.0 backend to get 2.0 support.
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
How is Mono? The sheer enormity of .Net framework makes me skeptical that it can be reliably ported.
On the other hand I would love to do a web mail app on Unix. Unix mail servers are far superior. Otherwise a NT asp.net app. would have to access not natively but via SMTP and POP/IMAP or some other solution like remoting.
Mono works fine for me. occasionally I hit a bug in the Framework. Generally they have a patch out within a day or so. There's actually an open source webmail app for .Net out there which runs on Mono. Generally the rule is that so long as you're not using
a library that requires P/Invoke or a windows dependant API, you're probably fine.
If the answer I provided is useful or informative please check the "answer" button.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Contributor
3938 Points
3276 Posts
Converting to Generics
Oct 10, 2005 12:09 PM|JeffreyABecker|LINK
currently the code looks something like this:
public abstract class DataLayerBase{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
public virtual Object Get(Object PrimaryKey);
//Returns an object which depends on the Type the derived class handles
// the parameter is the value of the PrimaryKey, it's type is dependent on the type
// that the derived class handles.
public virtual void Save(Object o);
public virtual void Delete(Object o);
}
generally in my derived classes i will have members along the lines of
public new ExampleObject Get(int ID); // ID is the primary key in the Table, it's of type Int32
public new void Save (ExampleObject o);
public new void Delete(ExampleObject o);
currently, im rewriting the class as such:
public abstract class DataLayerBase<T>{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
public virtual T Get(Object PrimaryKey);
public virtual void Save(T inst);
public virtual void Delete(T inst);
}
My questions are...
1) can i declare get such that it returns T, and takes a parameter who's type is the same as a particular field of the type the datalayer will be returning?
2) how do i go about declaring the derived class to handle a particular Type?
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 10, 2005 07:51 PM|rmprimo|LINK
I am not sure I understand what the benefit of Generics is in this case.
It doesn't appear to be a collection.
Rob
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 11, 2005 07:27 AM|JeffreyABecker|LINK
what a really want to say is something like...
public abstract class DataLayerBase<T> where T : IDataObject{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
// Assuming IDataObject declares PrimaryKeyField, Im Fairly sure this will not work
public virtual T Get(typeof(T.PrimaryKeyField) PrimaryKey);
public virtual void Save(T inst);
public virtual void Delete(T inst);
}
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 11, 2005 08:38 AM|rmprimo|LINK
public abstract class DataLayerBase<T, Tkey> where T : IDataObject, TKey: IYourDataKey<T>
{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
public virtual T Get(typeof(Tkey PrimaryKey);
public virtual void Save(T inst);
public virtual void Delete(T inst);
}
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 11, 2005 10:46 AM|JeffreyABecker|LINK
public class HighSchool
{
// private members
int _iID;
string _strPrinciple;
string _strName;
// public accessors
public int ID
{
get { return _iID;}
set { _iID = value; }
}
public string Principle
{
get { return _strPrinciple;}
set { _strPrinciple = value; }
}
public string Name
{
get { return _strName;}
set { _strName = value; }
}
}
(lets forget the where constraints for a sec...)
My understanding is that i could declare a class:
public class HighSchoolDataLayer : BaseDataLayer<HighSchool,System.Int32>{
public HighSchoolDataLayer (string ConnectionString):base(ConnectionString){
}
public override HighSchool Get (Int32 PrimaryKey)...
public override void Save(HighSchool inst) ...
public override void Delete(HighSchool inst) ...
}
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 11, 2005 11:22 AM|rmprimo|LINK
1. You are hardwiring the type int and defeating the purpose of a generic key type.(which inscidentally may be a good thing. SQL uses very few types and can accomplish a lot.)
2. In general, the most important part of generics - performance of collections of known types is not at play here. You don't have a collection. What is wrong with 'traditional' inheritance and both base and derived types have the same type for key?
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 11, 2005 11:57 AM|JeffreyABecker|LINK
In this case generics are being used because they allow me to declare DataLayerBase which doesnt care about Types and declare derived classes which only handle a single type.
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 11, 2005 12:18 PM|rmprimo|LINK
That is exactly what this does
public abstract class DataLayerBase<T, Tkey> :IKeyable<T, Tkey> where T : IDataObject
Tkey may be defined in the base class itself but probably better in an interface.
The main method in IKeyable ought to be:
public T GetByKey(Tkey key);
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 11, 2005 12:56 PM|JeffreyABecker|LINK
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 12, 2005 08:15 AM|rmprimo|LINK
The relationship still needs to be defined.
public T GetByKey(Tkey key);
You still ought to check in your db if you really have to have type safety of the key. It sure would save you a lot of complexity in the inheritance tree if you don't.
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 12, 2005 02:05 PM|JeffreyABecker|LINK
First I split DataLayerBase into an interface and an Abstract class
interface IDataAccessLayer{
Object Get(Object PrimaryKey);
void Save(Object value);
void Delete(Object value);
}
public abstract class DataAccessLayerBase{
private string _ConnectionString;
public DataLayerBase(string ConnectionString){
_ConnectionString = ConnectionString;
}
protected String ConnectionString{
get{return _ConnectionString;}
}
}
Derived DataAccessLayers then Explicitly Implement the Interface and Inherit the Base class.
as such:
public class HighschoolDataLayer: IDataAccessLayer, DataAccessLayerBase{
public HighschoolDataLayer(string ConnectionString): base(ConnectionString){
}
Highschool Get(int ID){
// ...
}
public void Save(HighSchool s){
//...
}
public void Delete(HighSchool s){
//...
}
// This is the explicit interface implementation. these methods will only be accessable
// if this object has been cast into an IDataAccessLayer. We still need to call the
// Real versions in case someone does cast into an IDataAccessLayer
Object IDataAccessLayer.Get(Object value){
if(value is Int32)
return this.Get((Int32)value);
}
void IDataAccessLayer.Save(Object value){
if(value is Highschool)
return this.Save((Highschool)value);
}
void IDataAccessLayer.Delete(Object value){
if(value is Highschool)
return this.Delete((Highschool)value);
}
}
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 12, 2005 02:27 PM|rmprimo|LINK
I am not a performance pro. The way you can tell the real perf pros is they never talk off the top of their heads. When you ask them a question they are already setting up all kinds of bench tests...
However I doubt if any collection of HighSchools(or their derivatives) would ever be represented by int, so casting would never mean unnecessary boxing and unboxing.
The only place you might is the key. Increasingly, I see fewer and fewer integer data type @@Identity columns. The Yukon samples are all Guids.
I guess that facilitates with merging conflicts and because of cheap storage. So chances are even there you wouldn't be penalized.
Then leave generics where they shine - in collections.
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 12, 2005 05:37 PM|JeffreyABecker|LINK
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
660 Points
494 Posts
Re: Converting to Generics
Oct 15, 2005 01:53 AM|nicequy|LINK
Generics rock and I believe they shine in more places than just in collections. I was a little disappointed to see that you dropped generics all together! [;)]
In addition, do you know about the new TableAdapters in 2.0....HOLY YES! They rock as well. Anyway, I wanted to throw my two cents out there. This is one way you could implement your BOM:
public class HighSchool {
int iD;
string principle;
string name;
public int ID {
get { return this.iD; }
set { this.iD = value; }
}
public string Principle {
get { return this.principle; }
set { this.principle = value; }
}
public string Name {
get { return this.name; }
set { this.name = value; }
}
}
public interface IDataAccessLayer<T, K> {
T Get(K primaryKey);
void Save(T item);
void Delete(T item);
}
public abstract class DataLayerBase {
private string connectionString;
public DataLayerBase(string connectionString) {
this.connectionString = connectionString;
}
protected string ConnectionString {
get { return this.connectionString; }
}
}
public class HighSchoolDataLayer : DataLayerBase, IDataAccessLayer<HighSchool, int> {
public HighSchoolDataLayer(string connectionString) : base(connectionString) { }
public HighSchool Get(int primaryKey) {
throw new NotImplementedException();
}
public void Save(HighSchool item) {
throw new NotImplementedException();
}
public void Delete(HighSchool item) {
throw new NotImplementedException();
}
}
In addition, you will notice that I was able to implement the interface implicitly which does not require the additional interface methods implementaion.
Hope this helps,
Jason
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 15, 2005 07:17 AM|rmprimo|LINK
1.What additional methods? There are only three and all accounted for.
2.What do you mean by implicit? That you didn't explicitly specify the interface members in which case you could have picked to inherit some and not all of the interface members like:
public class HighSchoolDataLayer : DataLayerBase {
public HighSchoolDataLayer(string connectionString) : base(connectionString) { }
public HighSchool IDataAccessLayer<HighSchool, int>.Get(int primaryKey) {
throw new NotImplementedException();
}
}
3. What is he accomplishing over non-generics besides some type rigor (not type safety) because now it is an interface he is inheriting from and implementing the the method bodies de novo?
This itself is a lot if the code is intended for distribution. However I believe he was looking to bake this logic into the base class and somehow have it inherited in a smart type safe way down the line, and that is not accomplished here as he needs to implement it in every descendant.
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 15, 2005 09:44 AM|JeffreyABecker|LINK
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.
Member
22 Points
700 Posts
Re: Converting to Generics
Oct 15, 2005 10:04 AM|rmprimo|LINK
Jeff,
How is Mono? The sheer enormity of .Net framework makes me skeptical that it can be reliably ported.
On the other hand I would love to do a web mail app on Unix. Unix mail servers are far superior. Otherwise a NT asp.net app. would have to access not natively but via SMTP and POP/IMAP or some other solution like remoting.
I heard System.Net.Mail is still tied to CDO.
Rob
Contributor
3938 Points
3276 Posts
Re: Converting to Generics
Oct 15, 2005 11:46 AM|JeffreyABecker|LINK
Warning: Code is often uncompiled and possibly started life written on the back of a napkin. Beware typos.