When FarmVille == Productivity

Update 2010-02-08: Jonathan Pryor has merged many of my extension methods into Cadenza. I’d strongly suggest checking it out.

It’s no secret to my friends that I love to program… even more so as I’ve been developing a FarmVille client in C# and having them test it. (As much as you might hate FarmVille, you must agree that there’s a certain awesome factor in LINQ-to-FarmVille: service.Plow(from i in service.World.Objects.OfType<Plot>() where i.State == "fallow" || i.State == "withered" select i)

Well, among this project and others I consistently find myself writing the same code over and over. I know of many programmers who have developed personal toolkits for the languages they use frequently, but for some reason I’ve been programming for about 13-15 years now and haven’t ever built my own library. This application gave me an excuse to do so, and so I’ve started on the Cdh.Toolkit suite of libraries.

Here is a summary of the classes available:

  • Cdh.Toolkit.Collections: Some useful collection types, all designed to be derived.
    • ReadOnlyCollection<T>, ReadOnlyDictionary<TKey, TValue>, and ReadOnlyList<T>: Wrappers around the corresponding interface types ICollection<T>, IDictionary<TKey, TValue>, and IList<T>, throwing exceptions on all write attempts. While there is a ReadOnlyCollection<T> as part of the .NET framework, it is not designed to be derived, and the other two classes do not have a counterpart at all.
    • SynchronizedCollection<T>, SynchronizedDictionary<TKey, TValue>, and SynchronizedList<T>: Wrappers around the corresponding interfaces. All accesses are synchronized against a ReaderWriterLockSlim, allowing for multiple concurrent read operations. The enumeration behavior can be specified as either lock, which holds a read lock for the duration of the enumeration, or copy, which creates a copy of the collection and enumerates it instead.
    • ObservableCollection<T>: A collection that fires events when modified. ObservableDictionary and ObservableList are currently not provided, due to some implementation complexities. However, the interfaces IObservableCollection<T>, IObservableDictionary<TKey, TValue>, and IObservableList<T> and some EventArgs classes are provided to allow developers to implement their own observable collections easily.
    • ObservableHashSet<T>: An observable and API-compatible wrapper around HashSet<T>.
    • ReadOnlyObservableCollection<T>, ReadOnlyObservableDictionary<TKey, TValue>, and ReadOnlyObservableList<T>: Wrappers around the IObservable* interfaces mentioned above. Events from the wrapped collections are forwarded. This allows one to have a read only observable collection without sacrificing the IObservable* interface, which would happen if such a collection were wrapped in one of the normal read only classes listed above.
  • Cdh.Toolkit.Extensions: Extension libraries designed to ease the use of many classes in the .NET framework.
    • Collections: Extensions specific to collection classes.
      • TValue IDictionary<TKey, TValue>.GetOrDefault(TKey key): Returns default(TValue) if the key is not present in the dictionary.
      • TValue IDictionary<TKey, TValue>.GetOrValue(TKey key, TValue fallback): Returns fallback if the key is not present in the dictionary.
    • ComponentModel: Extensions specific to the System.ComponentModel namespace.
      • void ISynchronizeInvoke.AutoInvoke(Action action): Executes the action delegate on the ISynchronizeInvoke object if required, otherwise does so on the current thread.
      • object ISynchronizeInvoke.AutoInvoke(Delegate method, params object[] args): Executes the delegate on the ISynchronizeInvoke object if required, otherwise does so on the current thread, and returns the return value of that method in either case.
      • AsyncCallback AsyncCallback.Invoked(ISynchronizeInvoke obj): Returns a wrapper around the AsyncCallback delegate that will invoke it using the AutoInvoke extension above. Useful for async callbacks that need to operate on a Winforms GUI.
    • Enumerable: Extensions to enumerable objects.
      • IEnumerable<T> IEnumerable<T?>.NotNull() where T : struct: Returns all values from the non-null nullable objects in the sequence.
      • void IEnumerable<T>.Walk(): Enumerates the sequence, discarding all values obtained. Useful as an alternative to .ToList() when you need to make sure that a query executes, but do not need to use the result.
      • void IEnumerable<T>.CopyInto(IList<T> list): Copies a sequence into a list.
    • Events: Extensions that make writing event logic easier. All of these extensions return if the event handler in question is null, making event-firing code simpler and easier to read.
      • void EventHandler.Fire(object sender): Uses EventArgs.Empty as the event arguments.
      • void EventHandler.Fire(object sender, EventArgs args)
      • void EventHandler.Fire(object sender, Func<EventArgs> argsFactory): Calls the factory function only if the event handler is not null. Useful when construction of the event arguments can take a long time.
      • void EventHandler<T>.Fire(object sender, T args)
      • void EventHandler<T>.Fire(object sender, Func<T> argsFactory): Calls the factory function only if the event handler is not null. Useful when construction of the event arguments can take a long time.
    • ReaderWriterLockSlim: Allows these kind of locks to be used in a using() block, which makes code easier to read and maintain. They will also return a no-op IDisposable if a compatible lock is already held by the current thread, which makes non-recursive lock objects simpler to code with. (The return type is actually a value type that implements IDisposable, which means that usage of these methods does not incur any object allocation overhead.)
      • IDisposable ReaderWriterLockSlim.Read(): Returns an IDisposable that will release the read lock when disposed. This method returns a no-op IDisposable instead if the current thread already holds a read, upgradeable read, or write lock.
      • IDisposable ReaderWriterLockSlim.UpgradeableRead(): Returns an IDisposable that will release the upgradeable read lock when disposed. This method returns a no-op IDisposable instead if the current thread already holds an upgradeable read or write lock.
      • IDisposable ReaderWriterLockSlim.Write(): Returns an IDisposable that will release the write lock when disposed. This method returns a no-op IDisposable instead if the current thread already holds a write lock.
    • Reflection
      • T ICustomAttributeProvider.GetCustomAttribute<T>(bool inherit) where T : Attribute: Returns a typed attribute, or null if there is no attribute of type T.
      • IEnumerable<T> ICustomAttributeProvider.GetCustomAttributes<T>(bool inherit) where T : Attribute: Returns a sequence of attributes of type T present on the attribute provider.
    • Reflection.Emit
      • void ILGenerator.EmitTypeOf(Type type): Emits the IL sequence that will leave the same Type object on the execution stack.

    The amount of code is slim, but I’ve found at least one of the classes or extensions invaluable in every project I’ve worked on since starting the toolkit. It’s an interesting case where coding for a game actually winds up improving my productivity working on other software too.

    Eventually these libraries will be released under the MIT license, so stay tuned for another blog post with a link to the Git repository.

    (And yes, the above list will be converted into real documentation. Someday.)

11 Replies to “When FarmVille == Productivity”

  1. Currently the client is available by invitation only. Foole, the source for the client will probably not be released since I am considering selling the client after doing some market research.

    The AMF/AMF3 library I wrote to enable serialization and deserialization of AMF streams is, however, free software: http://gitorious.org/amf/amf

  2. By any chance, would you like to merge your toolkit with Cadenza?

    http://gitorious.org/cadenza

    It’s MIT/X11 licensed, and already contains many of the types you listed (e.g. ReadOnlyDictionary, IEnumerable.Walk {though it’s called .Apply() in Cadenza}, etc.), your Reflection extension methods, etc.

    I’d love to get more contributors involved. 🙂

    1. That’s certainly a strong possibility, Jon. Looks like our toolkits have a lot of the same goals. My Collections namespace is quite a bit different than yours (e.g. ReadOnlyDictionary<TKey, TValue> inherits ReadOnlyCollection<KeyValuePair<TKey, TValue>>) but I’m sure we can still merge successfully. I’ll work on putting my code up today and we can go from there.

  3. Hello, Chris.
    I was searching for a client for FarmVille and I was happy to have found your website. But as I read on, I found out that the client is not yet released.
    I don’t know anything about programming, so the AMF streams you shared there are pretty useless to me.
    Any news about the release of the client?
    Or any other way of sharing it?

  4. @Sorin: The project is currently on hiatus as I have lost interest. Unfortunately, Facebook/Zynga change things up on a routine basis, so the old source code that I do have probably needs to be tweaked. I am looking at the possibility of resurrecting the client and offering it as a subscription service (something really cheap, but recurring monthly) since it does take quite a bit of effort to maintain the client.

  5. Why do you think that System.Collections.ObjectModel.ReadOnlyCollection isn’t meant to be derived?

    MSDN says,

    This base class is provided to make it easier for implementers to create a generic read-only custom collection. Implementers are encouraged to extend this base class instead of creating their own.

  6. WOW that sounded like an ambitious project to undertake. But as you said, with the constant tweaking with the code by Facebook and Zynga, it can be hard to dedicate time to stay active with the changing source code.

    Wondering if you ever went ahead with this or if it’s still on the backburner?

    All the best for the future either way!

    Eric Harrison

  7. @Eric: The project is still being maintained from time to time, but I haven’t yet decided on a release plan. I want to monetize the project somehow since it’s taken many, many hours of my life to produce, but I’m still trying to figure out the best way to do that.

Comments are closed.