INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

How to set collection from inside constructor

How to set collection from inside constructor

(OP)
I have a class and a list<class> that I want to set up in the constructor. I want to fill the collection when I instantiate the object.

CODE

public class DotNetType
    {
        public string DatabaseDataType { get; set; }
        public string DotNetDataType { get; set; }

    }

    public class DotNetTypeCollection : List<DotNetType>
    {
        public DotNetTypeCollection()
        {
            DbObject dbo = new DbObject();
            DataSet ds = null;
            ds = dbo.GetDataSet("dbGen.GetDotNetDataTypes");

            this = ds.Tables[0].AsEnumerable().Select(row => new DotNetType
                            {
                                DotNetDataType = row.Field<string>("DotNetDataType"),
                                DatabaseDataType = row.Field<string>("DatabaseDataType")
                            }).ToList();
        }
    } 

I get an error that "this" is readonly.

Can this be done?

Thanks,

Tom

RE: How to set collection from inside constructor

No, you can't overwrite the current object with a new one from that object. In this case, you'd be better off just doing something like this:

CODE

public class DotNetType
{
    public string DatabaseDataType { get; set; }
    public string DotNetDataType { get; set; }
 
}
 
public class DotNetTypeCollection : List<DotNetType>
{
    public DotNetTypeCollection() : base()
    {
        DbObject dbo = new DbObject();
        DataSet ds = null;
        ds = dbo.GetDataSet("dbGen.GetDotNetDataTypes");
 
        this.AddRange(ds.Tables[0].AsEnumerable().Select(row => new DotNetType
        {
            DotNetDataType = row.Field<string>("DotNetDataType"),
            DatabaseDataType = row.Field<string>("DatabaseDataType")
        }).ToList());
    }
} 


For future reference though, the general 'good practice' for doing something like this, where you need to basically make a constructor that returns a new instance of the object, is by making a static method instead. Keep in mind however that your example isn't really a good use case for doing it this way; a normal constructor works just fine for you.

CODE

public class DotNetType
{
    public string DatabaseDataType { get; set; }
    public string DotNetDataType { get; set; }
 
}
 
public class DotNetTypeCollection : List<DotNetType>
{
    private DotNetTypeCollection() : base() { }
 
    public static DotNetTypeCollection CreateDotNetTypeCollection()
    {
        DbObject dbo = new DbObject();
        DataSet ds = null;
        ds = dbo.GetDataSet("dbGen.GetDotNetDataTypes");
 
        DotNetTypeCollection collection = new DotNetTypeCollection();
 
        collection.AddRange(ds.Tables[0].AsEnumerable().Select(row => new DotNetType
        {
            DotNetDataType = row.Field<string>("DotNetDataType"),
            DatabaseDataType = row.Field<string>("DatabaseDataType")
        }).ToList());
 
        return collection;
    }
} 

RE: How to set collection from inside constructor

(OP)
That did exactly what I wanted. I couldn't figure out what to use "this" with to add the collection. "this.Addrange" worked perfectly.

Now you said the normal good practice was to do it the 2nd way with the static method, but that my example wasn't a good case for doing it this way. Why not? I prefer the 1st way, but when would I do it the 1st way and when the 2nd?

I had a similar case that I did which also did it the 1st way, but I couldn't figure out how to get this one to work that way:

CODE

public class StatementCauseCollection : List<StatementClause>
    {
        public StatementCauseCollection(int objectID, string objectType)
        {
            SetUpStatementClauseList(objectID, objectType);
        }
        
        private StatementCauseCollection()
        {
        }

        public void SetUpStatementClauseList(int objectID, string objectType)
        {
            if (objectType == "U" || objectType == "V")
            {
                this.Add(new StatementClause(objectID, StatementClauseTypes.Where));
                this.Add(new StatementClause(objectID, StatementClauseTypes.OrderBy));
                this.Add(new StatementClause(objectID, StatementClauseTypes.Select));
                if (objectType == "U")
                {
                    this.Add(new StatementClause(objectID, StatementClauseTypes.Insert));
                    this.Add(new StatementClause(objectID, StatementClauseTypes.Update));
                    this.Add(new StatementClause(objectID, StatementClauseTypes.Delete));
                }
            }
        }
    } 

Thanks,

Tom

RE: How to set collection from inside constructor

Jon Skeet summarizes reasons for doing and not doing this quite nicely, at least in my opinion.

http://stackoverflow.com/questions/194496/static-c...

In your case, there isn't really a good reason to go with a factory method. I typically do that in scenarios like... when working with Micros, the communication are all done over TCP, using Message Framing. I have everything I need all wrapped up in a class library. I don't want someone using the Message class to be able to construct a message themselves; they must provide the ASCII string received from the TCP socket. If it isn't a valid format, I want to be able to make the instance null, which makes for a very easy check. So in this case, I did something like this:

CODE

namespace ConsoleApplication1
{
    class MicrosMessage
    {
        private MicrosMessage() { }
 
        public int WorkstationNumber { get; private set; }
 
        public static MicrosMessage MessageFromString(string rawData)
        {
            if (!IsValidMessage(rawData))
                return null;
            MicrosMessage mm = new MicrosMessage();
            mm.WorkstationNumber = 10;
            //do work here....
            return mm;
        }
 
        private static bool IsValidMessage(string data)
        {
            return true;
        }
    }
} 

Notice I made the empty constructor private - that means the ONLY way to create an instance of MicrosMessage is by calling my factory method. I also made my one attribute with a public getter, and private setter, which means that the values can ONLY be set from within the MicrosMessage class, which means they can't accidentally overwrite the information anywhere. I could do the same by only exposing a single constructor that requires a string, but now I have to also factor in doing things like creating something like an IsValid attribute to check against - the new constructor ALWAYS creates a new instances, excepting when an exception gets thrown.

This could be a good use case for you if you need a way to check for failures, for example, but it doesn't look like that is the case here? There is also, of course, design preference. Some people prefer sticking to the new Object() paradigm, and other like using the static methods because it can make things clearer.

RE: How to set collection from inside constructor

I don't think it's a good use-case because typically you'd only do something like that when you need to pass it some information, and get a new object from it. It is usually something you'd do when you want to prevent creating a new instance of the object with the default settings, and enforce creating the object is specific ways, though technically you can do that with normal constructors, it would get VERY confusing trying to create those overloads:

CODE

public class MicrosDatabase : IDisposable
{
    private IDbConnection _IDbConnection;
 
    private MicrosDatabase() { }
 
    public static MicrosDatabase OdbcDbConnection()
    {
        return MicrosDatabase.OdbcDbConnection("custom", "custom", "micros");
    }
    public static MicrosDatabase OdbcDbConnection(string Username, string Password)
    {
        return MicrosDatabase.OdbcDbConnection(Username, Password, "micros");
    }
    public static MicrosDatabase OdbcDbConnection(string Username, string Password, string DSN)
    {
        return new MicrosDatabase()
        {
            _IDbConnection = new System.Data.Odbc.OdbcConnection(string.Format("DSN={0};UID={1};PWD={2}", DSN, Username, Password))
        };
    }
    public static MicrosDatabase OleDbConnection(string Username, string Password)
    {
        return MicrosDatabase.OleDbConnection(MicrosOleProvider.RES5, Environment.MachineName, Username, Password, "127.0.0.1");
    }
    public static MicrosDatabase OleDbConnection(MicrosOleProvider Provider, string ServerName, string Username, string Password, string IPAddress)
    {
        return new MicrosDatabase()
        {
            _IDbConnection = new System.Data.OleDb.OleDbConnection(
                string.Format("Provider={0};EngineName=sql{1};UID={2};PWD={3};Links=TCPIP(Host={4})",
                    Provider, ServerName, Username, Password, IPAddress))
        };
    } 

So as you can see, I'm using a generic IDbConnection; once I get it constructed, it doesn't matter how I got connected. I can run queries and stored procedures and what not exactly the same way; the only time I need to be concerned with the connection method is when the user is creating it, but I need a way to have what almost amounts to typed constructors. It would be very difficult to create proper construction overloads for this because of the sheer amount of overlap there is. This is only a slice from this object, because this class handles connection to two different database types using several different methods for both.

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close