The only thing I can think of to help you is to make your asserts use #if DEBUG
in their bodies so that the methods are empty at release.
e.g.
public static void AssertUIThread()
{
#if DEBUG
//the code goes here
#endif
}
That way you can check during development if you're calling methods appropriately, and the JIT will remove the call entirely in your production code.
I don't see a way to do this at compile-time at the moment, but I'm favoriting this question in the hopes that it'll be answered.
Edit:
The more I think about it, the more I think you might be able to do what you want using a custom FxCop rule post-compilation. The thing is... I don't know the Introspection API that FxCop provides, and it's not well documented. Or rather, it's not documented at all. The best I can do for you is provide a tutorial or two that may or may not help you. I'm currently in the middle of reading them; if I find something interesting, I'll post it.
Edit 2:
Ahah! You can analyze the caller and the callees of a method. Using the tutorial specified there, create an attribute specifically for methods that should always be called from the UI thread, and another one for methods that should only be called from a separate thread. Your custom rule checks for one of these attributes and only runs if a method has the attribute. It then analyzes the callers of that method (and their callers, and so forth, recursively) until it can determine that the caller was either on the UI thread or from a new thread.
Now we've come to the tricky part. I haven't been able to figure this part out yet, and I leave it to you to see what you can come up with, since it's late and I can't devote much time to the problem but I'm very much interested in the solution. The problem I keep running into is that threads are all started using delegates, and I get the feeling there will be trouble going further up the caller chain that those delegates. I don't know if it'll be possible to get to the delegate; if it were possible, the delegate type could be compared to known threading delegates to determine if the call was made on a new thread or not.
Even if that's possible, there'd be the problem of going through the delegate. If you can't, you can only be certain up to the first delegate whether or not something is on a new thread.
So, problems to solve. But, hopefully, a first step for you.