tags:

views:

13913

answers:

8

Here's what MSDN has to say under "When to Use Static Classes":

static class CompanyInfo
{
    public static string GetCompanyName() { return "CompanyName"; }
    public static string GetCompanyAddress() { return "CompanyAddress"; }
    //...
}

Use a static class as a unit of organization for methods not associated with particular objects. Also, a static class can make your implementation simpler and faster because you do not have to create an object in order to call its methods. It is useful to organize the methods inside the class in a meaningful way, such as the methods of the Math class in the System namespace.

To me, that example doesn't seem to cover very many possible usage scenarios for static classes. In the past I've used static classes for stateless suites of related functions, but that's about it. So, under what circumstances should (and shouldn't) a class be declared static?

+7  A: 

For C# 3.0, extension methods may only exist in top-level static classes.

Mark Cidade
A: 

I only use static classes for helper methods, but with the advent of C# 3.0, I'd rather use extension methods for those.

I rarely use static classes methods for the same reasons why I rarely use the singleton "design pattern".

jonnii
I'd rather not use extension methods for helper methods. Extension methods are messy, confusing for other developers and not generally intuitive across other languages. Extension methods have their purpose, but not as generic helper methods imho.
Mark S. Rasmussen
Just to clarify, by helper method I mean something like: string.ToSentence(), or string.Camelize(). Essentially anything that would live in a class like StringHelpers.
jonnii
Extension methods put you on a slippery slope...I think if you find yourself adding more than 2 extension methods for a given type, it might be time to examine why.
Jason Bunting
Those two examples, I would especially not like. The more generic the type, the less suitable it is for extension methods. The worst example is beginning to add ToInt32() method to Object, ToDateTime() etc. I'd much rather have these in separate classes.
Mark S. Rasmussen
+53  A: 

I wrote my thoughts of static classes in an earlier thread:
http://stackoverflow.com/questions/205689/class-with-single-method-best-approach#206481

I used to love utility classes filled up with static methods. They made a great consolidation of helper methods that would otherwise lie around causing redundancy and maintenance hell. They're very easy to use, no instantiation, no disposal, just fire'n'forget. I guess this was my first unwitting attempt at creating a service oriented architecture - lots of stateless services that just did their job and nothing else. As a system grows however, dragons be coming.

Polymorphism

Say we have the method UtilityClass.SomeMethod that happily buzzes along. Suddenly we need to change the functionality slightly. Most of the functionality is the same, but we have to change a couple of parts nonetheless. Had it not been a static method, we could make a derivate class and change the method contents as needed. As it's a static method, we can't. Sure, if we just need to add functionality either before or after the old method, we can create a new class and call the old one inside of it - but that's just gross.

Interface woes

Static methods cannot be defined through interfaces for logic reasons. And since we can't override static methods, static classes are useless when we need to pass them around by their interface. This renders us unable to use static classes as part of a strategy pattern. We might patch some issues up by passing delegates instead of interfaces.

Testing

This basically goes hand in hand with the interface woes mentioned above. As our ability of interchanging implementations is very limited, we'll also have trouble replacing production code with test code. Again, we can wrap them up but it'll require us to change large parts of our code just to be able to accept wrappers instead of the actual objects.

Fosters blobs

As static methods are usually used as utility methods and utility methods usually will have different purposes, we'll quickly end up with a large class filled up with non-coherent functionality - ideally, each class should have a single purpose within the system. I'd much rather have a five times the classes as long as their purposes are well defined.

Parameter creep

To begin with, that little cute and innocent static method might take a single parameter. As functionality grows, a couple of new parameters are added. Soon further parameters are added that are optional, so we create overloads of the method (or just add default values, in languages that support them). Before long, we have a method that takes 10 parameters. Only the first three are really required, parameters 4-7 are optional. But if parameter 6 is specified, 7-9 are required to be filled in as well... Had we created a class with the single purpose of doing what this static method did, we could solve this by taking in the required parameters in the constructor, and allowing the user to set optional values through properties, or methods to set multiple interdependent values at the same time. Also, if a method has grown to this amount of complexity, it most likely needs to be in its own class anyways.

Demanding consumers to create an instance of classes for no reason

One of the most common arguments is, why demand that consumers of our class create an instance for invoking this single method, while having no use for the instance afterwards? Creating an instance of a class is a very very cheap operation in most languages, so speed is not an issue. Adding an extra line of code to the consumer is a low cost for laying the foundation of a much more maintainable solution in the future. And finally, if you want to avoid creating instances, simply create a singleton wrapper of your class that allows for easy reuse - although this does make the requirement that your class is stateless. If it's not stateless, you can still create static wrapper methods that handle everything, while still giving you all the benefits in the long run. Finally, you could also make a class that hides the instantiation as if it was a singleton: MyWrapper.Instance is a property that just returns new MyClass();

Only a Sith deals in absolutes

Of course, there are exceptions to my dislike of static methods. True utility classes that do not pose any risk to bloat are excellent cases for static methods - System.Convert as an example. If your project is a one-off with no requirements for future maintenance, the overall architecture really isn't very important - static or non static, doesn't really matter - development speed does, however.

Standards, standards, standards!

Using instance methods does not inhibit you from also using static methods, and vice versa. As long as there's reasoning behind the differentiation and it's standardised. There's nothing worse than looking over a business layer sprawling with different implementation methods.

Mark S. Rasmussen
Steve Yegge has written about it as well. But his post is wayyy longer than could be post here. If you're still not convinced, try http://steve.yegge.googlepages.com/singleton-considered-stupid
chakrit
Has anyone ever noticed that the statement, "Only a Sith deals in absolutes," is an absolute?Sorry. Couldn't help it.
John Kraft
Just as I go out on a limb to get my point across, so does Yegge. Some of his points comes down to normal programming consideration - if the singleton keeps valuable resources open, that may be a problem, of course. Given careful consideration, there are places for singletons and statics alike.
Mark S. Rasmussen
+1  A: 

I use static classes as a means to define "extra functionality" that an object of a given type could use under a specific context. Usually they turn out to be utility classes.

Other than that, I think that "Use a static class as a unit of organization for methods not associated with particular objects." describe quite well their intended usage.

Trap
+3  A: 

I do tend to use static classes for factories. For example, this is the logging class in one of my projects:

public static class Log
{
   private static readonly ILoggerFactory _loggerFactory =
      IoC.Resolve<ILoggerFactory>();

   public static ILogger For<T>(T instance)
   {
      return For(typeof(T));
   }

   public static ILogger For(Type type)
   {
      return _loggerFactory.GetLoggerFor(type);
   }
}

You might have even noticed that IoC is called with a static accessor. Most of the time for me, if you can call static methods on a class, that's all you can do so I mark the class as static for extra clarity.

Rob
+2  A: 

If you use code analysis tools (ex: fxcop) it will recommend that you make your method(s) static if you don't access instance data. The rationale is that there is a perf gain. ref: http://msdn.microsoft.com/en-us/library/ms245046(VS.80).aspx.

more of a guideline then a rule, really ...

A: 

static classes are like Highlander; there can only be one.

Karl R
A: 

I have a static class "MembershipUsers" to always keep the current list of users on my site in a List of type which holds string username, string display name.

I also use this class to generate the HTML for paging and to sort the List (Alphabetically etc.)

Anything wrong with this? I find that i can port this across many apps?


{ private string _uName; private string _comment; private static List userList;

    public string UName
    {
        get
        {
            return _uName;
        }
        set
        {
            _uName = value;
        }
    }

    public string Comment
    {
        get
        {
            return _comment;
        }
        set
        {
            _comment = value;
        }
    }

    //populate the static userList object with the site's users
    public static List<MyUser> getUsers()
    {
        userList = new List<MyUser>();

        List<string> Users = System.Web.Security.Roles.GetUsersInRole("User").ToList();
        Users.AddRange(System.Web.Security.Roles.GetUsersInRole("OpenIDUser").ToList());

        foreach (string username in Users)
        {
            System.Web.Security.MembershipUser aUser = System.Web.Security.Membership.GetUser(username);

            MyUser theUser = new MyUser();
            theUser.UName = aUser.UserName;
            theUser.Comment = aUser.Comment;
            userList.Add(theUser);
        }

        //sort the list in alphabetical order
        Sort();

        return userList;
    }

    //overloaded method that takes in a queryString
    public static string searchForUsers(string criteria)
    {
        int usersfound = 0;
        List<MyUser> theList = getUsers();
        userList = new List<MyUser>();
        userList.TrimExcess();

        foreach (MyUser user in theList)
        {
            System.Web.Security.MembershipUser aUser = System.Web.Security.Membership.GetUser(user.UName);
            bool add = false;
            //if the user is an openid user search the display
            //name else searh the username
            if (Roles.IsUserInRole(aUser.UserName, "User"))
            {
                if (aUser.UserName.Contains(criteria))
                {
                    add = true;
                }
            }
            else if (Roles.IsUserInRole(aUser.UserName, "OpenIDUser"))
            {
                if (aUser.Comment.Contains(criteria))
                {
                    add = true;
                }
            }
            //if found add to the userList
            if (add)
            {
                usersfound++;
                MyUser theUser = new MyUser();
                theUser.UName = aUser.UserName;
                theUser.Comment = aUser.Comment;
                userList.Add(theUser);
            }
        }
        //sort the list in alphabetical order
        Sort();
        //after the search list has beemn created return the first page
        if (usersfound > 0)
        {
            return getAPage(0, 2);
        }
        else
        {
            return "No users found given \""+criteria+"\"";
        }
    }

    private static void Sort()
    {
        //bubble sort performance?
        for (int i = 0; i < userList.Count(); i++)
        {
            for (int j = 0; j < userList.Count(); j++)
            {
                try
                {
                    if (userList.ElementAt(i).Comment.CompareTo(userList.ElementAt(j).Comment) == -1)
                    {
                        MyUser temp = userList.ElementAt(i);
                        userList.RemoveAt(i);
                        userList.Insert(j, temp);

                    }
                }
                catch { }

            }

        }
    }

    //returns how many users are in the list
    public static int getNumberOfUsers()
    {
        return userList.Count();
    }

    //using the current list get the number of pages
    //and return the appropriate HTML
    public static string getPages(int skip, int take)
    {
        string toReturn = "";

        //create the page links
        int nrusers = getNumberOfUsers();
        int nrpages = 0;

        //if numbner of users / pages is even pages are even else = + 1;
        if (nrusers % take == 0)
        {
            nrpages = nrusers / take;
        }
        else
        {
            nrpages = (nrusers / take) + 1;
        }

        //for every page display its HTML
        //if you are viewing page 3 you shd not
        //be able to click it again
        for (int j = 0; j < nrpages; j++)
        {
            if (skip != j * 2)
            {
                toReturn += "<a style=\"cursor:hand;cursor:pointer;\" onclick=\"showPage('" + (j) * take + "','"+2+"')\" >" + (j + 1) + "&nbsp;</a>";
            }
            else
            {
                toReturn += j + 1 + "&nbsp;";
            }
        }
        if (nrpages != 1)
        {
            return toReturn;
        }
        else
        {
            return "";
        }
    }

    //return an HTML list of online users
    public static string whosOnline()
    {
        string output = "";
        for (int i = 0; i < getNumberOfUsers(); i++)
        {
            System.Web.Security.MembershipUser myUser2 = System.Web.Security.Membership.GetUser(userList[i].UName);
            //do the if online stuff
            if (myUser2.IsOnline)
            {
                if (Roles.IsUserInRole(myUser2.UserName, "User"))
                {
                    output += "<a href=\"UserProfile.aspx?id=" + myUser2.UserName + " \" >" + myUser2.UserName + "</a>,&emsp;";
                }
                else if (Roles.IsUserInRole(myUser2.UserName, "OpenIDUser"))
                {
                    output += "<a href=\"UserProfile.aspx?id=" + myUser2.UserName + " \" >" + myUser2.Comment + "</a>,&emsp;";
                }
            }
        }

        return output;
    }

    public static string getAPage(int skip, int take)
    {

        string output = "";
        //if the userList is not instantiated instantiate it
        if (userList == null)
        {
            getUsers();
            output = getAPage(skip,take);
        }
        else
        {
            output = "<table cellpadding=\"10\" cellspacing=\"10\" ><thead><th >Username</th><th >Joined</th><th style=\"text-align:center\">Ratings</th><th >Last Seen</th></thead><tbody>";
            int i = 0;
            while (i < take && skip + i < userList.Count())
            {
                //get the username and use it to return a MembershipUser object
                string username = "";
                string comment = "";
                try
                {
                    username = ((MyUser)userList.ElementAt(skip + i)).UName;
                    comment = ((MyUser)userList.ElementAt(skip + i)).Comment;
                }
                catch { break; }

                System.Web.Security.MembershipUser myUser = System.Web.Security.Membership.GetUser(username);

                output += "<tr>";
                //username
                if (Roles.IsUserInRole(username, "User"))
                {
                    output += "<td><a href=\"UserProfile.aspx?id=" + myUser.UserName + "\" >" + myUser.UserName + "</a></td>";
                }
                else
                {
                    output += "<td><a href=\"UserProfile.aspx?id=" + myUser.UserName + "\" >" + myUser.Comment + "</a></td>";
                }

                //joined
                output += "<td>" + myUser.CreationDate + "</td>";
                //ratings
                output += "<td style=\"text-align:center\" >" + Common.getuserRatings(myUser.UserName) + "</td>";
                //last seen
                output += "<td>" + myUser.LastActivityDate + "</td>";

                output += "</tr>";
                i++;
            }
            output += "</tbody></table>";

            //also display the pages
            output += getPages(skip, take);

        }

        return output;
    }
The_Butcher