2

I'm having trouble finding information on this topic, possibly because I'm not sure how to phrase the question. Hopefully the braintrust here can help or at least advise. This situation might just be me being retentive but it's bugging me so I thought I'd ask for help on how to get around it.

I have a C# library filled with utility classes used in other assemblies. All of my extensions reside in this library and it comes in quite handy. Any of my other libraries or executables that need to use those classes must naturally reference the library.

But one of the extensions I have in there is an extension on the Control class to handle cross thread control updates in a less convoluted fashion. As a consequence the utility library must reference System.Windows.Forms.

The problem being that any library or executable that references the utilities library must now have a reference to System.Windows.Forms as well or I get a a build error for the missing reference. While this is not a big deal, it seems sort of stupid to have assemblies that have nothing to do with controls or forms having to reference System.Windows.Forms just because the utilities library does especially since most of them aren't actually using the InvokeAsRequired() extension I wrote.

I thought about moving the InvokeAsRequired() extension into it's own library, which would eliminate the System.Windows.forms problem as only assemblies that needed to use the InvokeAsRequired() extension would already have a reference to SWF.... but then I'd have a library with only one thing in it which will probably bother me more.

Is there a way around this requirement beyond separating out the 'offending' method and creating a nearly empty library? Maybe a compile setting or something?

  • It should be noted that the 'offending method' is actually used across multiple projects that have UI. A lot of the UI updates I do are as a result of events coming in and trying to update windows form controls from another thread causes various UI thread problems. Hence the method handling the Invoke when needed. (Though personally I think that whole InvokeRequired pattern should be wrapped up into the control itself rather than having something external do the thread alignment in the first place).
Ed Kramer
  • 131
  • 1
  • 12
  • 2
    Well, good, it doesn't belong in a library. You should do everything possible to get rid of it completely. – Hans Passant Dec 31 '14 at 18:10
  • See [this](http://stackoverflow.com/a/16364022/643085). It's WPF but you can apply the same pattern to a winforms app. BTW, yes, please, decouple all your code from winforms immediately, since it's totally deprecated and useless and it won't even be a part of future .Net – Federico Berasategui Dec 31 '14 at 18:36
  • 6
    "it won't even be a part of future .Net" -- cite, please. There has been no public statement by Microsoft that could be construed to support this claim. – Peter Duniho Dec 31 '14 at 19:31
  • 2
    It sounds like your library should be called `KitchenSink` - this is often the problem with utility libraries. I would probably just remove it completely, but if you do want it as a reusable utility, I prefer a couple DLLs separated by technology with potentially one thing in them over one DLL with _everything_. We have a network nuget repository for these and it works well – Rhumborl Dec 31 '14 at 21:42
  • AFAIK you need a reference only when you access that class (somehow). Move winforms stuff to a separate namespace and leave it alone there. Of course any other proposed solution is better if you have more than few methods. – Adriano Repetti Dec 31 '14 at 22:18
  • Which part doesn't belong in a library? the reference to forms? or the method itself. If you mean the method itself, it would still need to be somewhere that can be referenced by multiple projects. – Ed Kramer Jan 02 '15 at 17:07
  • @PeterDuniho Sorry, didn't see your comment before because you didn't @ me. Basically yes, try to do any winforms based stuff in WinRT, for example. Not to mention why would I want to take a dependency on stupid dinosaur useless winforms from the 60's that doesn't support anything when I can use a modern framework like WPF instead. – Federico Berasategui Jan 02 '15 at 20:29
  • 3
    @HighCore: "try to do any winforms based stuff in WinRT" -- except, WinRT isn't .NET (it's not even WPF), and lack of Winforms on WinRT in no way suggests Winforms itself is any more likely to be excluded from a future version of .NET than WPF is. In other words, your dogma is showing (again...frankly, your continued off-topic rants against Winforms and zealous proselytization of WPF are tiresome). – Peter Duniho Jan 02 '15 at 20:53

3 Answers3

2

If it's just one function, then package it up as a source code file into a NuGet package and then add the NuGet package to your projects. Then, this code will be easily deployable to new projects (as well as easily updateable), but you don't need to create a separate assembly. Just compile it into your application.

You would then store your NuGet package in a local NuGet repository, or get a myget account, or even just store it somewhere on your network. Worst case, you can check it into your version control, but I would just check in the "project" that you build the nuget package from, so you can rebuild the package if need be.

Who knows, at some point, you may add more utility functions that require windows forms, and at that point you could justify a separate assembly.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
0

It's easy: you have to move the offending code out. For now it might be a little concern, but in the end it might be a blast you did it now instead of at the moment you are forced to.

Even if it is just one method (for now), just move the method into another assembly. I didn't say a new one, it can be in the assembly that uses it if only one, or all others that need that moment derive from it.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
0

You can solve your problem by switching the utility library code from the early-binding pattern to the late-binding pattern when it comes to types declared in the System.Windows.Forms namespace.

This article shows how to do it the short way: Stack Overflow: C#.NET - Type.GetType(“System.Windows.Forms.Form”) returns null

And this code snippet shows how the monoresgen tool from the Mono Project (open source ECMA CLI, C# and .NET implementation) solves the System.Windows.Forms dependency problem.

public const string AssemblySystem_Windows_Forms = "System.Windows.Forms, Version=" + FxVersion + ", Culture=neutral, PublicKeyToken=b77a5c561934e089";

// ...

static Assembly swf;
static Type resxr;
static Type resxw;

/*
 * We load the ResX format stuff on demand, since the classes are in 
 * System.Windows.Forms (!!!) and we can't depend on that assembly in mono, yet.
 */
static void LoadResX () {
    if (swf != null)
        return;
    try {
        swf = Assembly.Load(Consts.AssemblySystem_Windows_Forms);
        resxr = swf.GetType("System.Resources.ResXResourceReader");
        resxw = swf.GetType("System.Resources.ResXResourceWriter");
    } catch (Exception e) {
        throw new Exception ("Cannot load support for ResX format: " + e.Message);
    }
}

// ...

static IResourceReader GetReader (Stream stream, string name, bool useSourcePath) {
    string format = Path.GetExtension (name);
    switch (format.ToLower (System.Globalization.CultureInfo.InvariantCulture)) {
    // ...
    case ".resx":
        LoadResX ();
        IResourceReader reader = (IResourceReader) Activator.CreateInstance (
            resxr, new object[] {stream});
        if (useSourcePath) { // only possible on 2.0 profile, or higher
            PropertyInfo p = reader.GetType ().GetProperty ("BasePath",
                BindingFlags.Public | BindingFlags.Instance);
            if (p != null && p.CanWrite) {
                p.SetValue (reader, Path.GetDirectoryName (name), null);
            }
        }
        return reader;
    // ...
    }
}

Snippet source: https://github.com/mono/mono/blob/mono-3.10.0/mcs/tools/resgen/monoresgen.cs#L30

Community
  • 1
  • 1
xmojmr
  • 8,073
  • 5
  • 31
  • 54