开发者

why does my C# client that uses Library A need to have a using statement for Library B (which A uses)

开发者 https://www.devze.com 2022-12-31 17:34 出处:网络
I have: Main Program Class - uses Library A Library A - has partial classes which mix in methods from Library B

I have:

  • Main Program Class - uses Library A
  • Library A - has partial classes which mix in methods from Library B
  • Library B - mix in methods & interfaces

So in Library B when I include a partial Node class which implements INode (defined in Library B) I suddenly get an error in my main class where it uses Node from Library A. The error tells me in the Main Class I have to have a using statement to Library B.

Any ideas?

EDIT - Except from code

    // *** PROGRAM ***
    class Program
    {
        static void Main(string[] args)
        {
            var context = new Model1Container();
            Node myNode;  // ** WITHOUT A using for Library B I have an error here ***
         }
     }


// ** LIBRARY A
namespace TopologyDAL
{
    public partial class Node
    {
        // Auto generated from EF
    }

    public partial class Node : INode<int>   // to add extension methods from Library B
    {
        public int Key
    }
}

// ** LIBRARY B
namespace ToplogyLibrar开发者_开发知识库y
{
    public static class NodeExtns
    {
        public static void FromNodeMixin<T>(this INode<T> node) {
           // XXXX
        }
    }
    public interface INode<T> 
    {
        // Properties
        T Key { get; }

        // Methods
    }

}

EDIT 2 - To clarify was it a reference or using error:

So the error that appears against the "Node myNode;" line is:

Error 1 The type 'Topology.INode`1' is defined in an assembly that is not referenced. You must add a reference to assembly 'Topology, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. U:\My Dropbox\source\ToplogyLibrary\TopologyDAL_ConsoleTest\Program.cs 11 13 TopologyDAL_ConsoleTest

When I let VS fix it for me it adds the Library2 as a reference. That is there is no "using " either before or after in the client code. So the issue is a Reference not Using issue.

EDIT 3 - Not specifically about the question, however I now notice that in the program project, I can't see the mixin methods (from Library B) unless I have a using statement to Library B? I might create a separate question re this.


My understanding of what's happening is that you only reference Library A from Main program and compiler tells you to add reference to Library B because some types that Library A exposes are defined in Library B.

To fix this add a reference to Library B to Main program project.

Here is little diagram. If Library A exposes a type defined in Library B then Main must reference Library B as well. The below situation will not work:

_____________             _____________               _____________
| Main       |references  | Library A  |references    | Library B  |
|           -|------------|->         -|--------------|->          |
|            |            | public     |              | SomeType   |
|            |            |  SomeType  |              |            |
|            |            |            |              |            |
-------------             -------------               -------------

This is only an issue when a type defined in Library B is accessible through Library A. This will be in one of the following situations:

  • EDITED Type defined in Library A (Node) derives from a type in Library B(INode<int>).
  • Method defined in Library A uses a type from Library B as return type or as an argument.
  • Type defined in Library A exposes a type from Library B as a property or a public field.

You will need to add a reference to Assembly3 from Assembly1 to make it compile.

_____________             _____________               _____________
| Main       |references  | Library A  |references    | Library B  |
|           -|------------|->         -|--------------|->          |
|            |            | public     |              | SomeType   |
|            |references  |  SomeType  |              |            |
|           -|------------|------------|--------------|->          |
|            |            |            |              |            |
-------------             -------------               -------------


If LibraryA exposes properties, returns objects from methods, or take parameters of types that are defined in LibraryB, and you declare or use a variable in Main Program of one of those types, you'll need the using statement.


Since Library A is using mix in method and interfaces from Library B, a using statement is required if Library A and Library B exist in different namespaces, this is just how C# works. The compiler needs to know where to find the Library B types that are used in Library A.


All you're using is the Node class from Library A, that's true. However, part of the definition of the Node class is the fact that it implements the INode<int> interface from Library B.

The very fact of the existence of the Node class demands that you include a reference to Library B.


As other answers have correctly explained, your main program needs the reference to Library B, because Library A has exposed a Library B type through its own public API. I.e. Node publicly implements INode<T>, causing that type to be visible to the main program.

Normally, the correct solution is simply to add the reference for Library B to your main program. After all, the main program will certainly need Library B at run-time, even if it does not itself directly access any of the types declared in that library.

But there is an alternative if for some reason adding that reference is problematic, assuming you can at least change Library A. That is to encapsulate the use of the type(s) from Library B so that they are not visible to the code using Library A. Generally, this encapsulation will involve some kind of proxy or wrapper, to hide the use of the third library and (in some cases) to expose the third library's features to the assembly referencing the second library.

For example, you could change Library A to look like this:

namespace TopologyDAL
{
    public partial class Node
    {
        // Auto generated from EF
    }

    public partial class Node
    {
        public int Key { ... }

        private class NodeProxy : INode<int>
        {
            private readonly Node _node;

            public NodeProxy(Node node)
            {
                _node = node;
            }

            public int Key { get { return _node.Key; } }
        }

        private readonly NodeProxy _nodeProxy;

        public Node()
        {
            _nodeProxy = new NodeProxy(this);
        }
    }
}

Then anywhere you want to use the extension method from Library B, you just use the _nodeProxy member instead of this.

Of course, the above assumes that the main program is in fact not using anything directly from Library B, and that all usages of types from Library B are found only in Library A, not even being exposed from Library A in return values, property types, etc. But what if that's not the case? What if, for example, you want the main program to have access to the extension method from Library B without referencing that library?

Well, my first answer would be "just go ahead and reference the assembly already!" :) But for the sake of argument, let's say you can't do that for some reason, or it at least is very inconvenient to do so. Then you can extend the proxy approach to add proxy methods to your Library A type, which in turn delegate to the implementation in Library B.

Extending the above for example, one would add to the Node class declaration another method:

public void FromNodeMixin()
{
    _nodeProxy.FromNodeMixin();
}

Note that the method itself adds no public usages of Library B. Instead, it uses the (previously explained) proxy instance to delegate the call to the extension method declared in Library B. In this way, only Library A actually needs to have the reference to Library B; the main program can reference Library A without being exposed to any types in Library B and thus not need the explicit reference.


Now all that said, I'll reiterate that there is in fact value in not working around the issue. For one, the adding of proxies can be time-consuming. It's fine for just a type or two, with maybe a few methods. But it's going to get real old real fast if you are trying to hide every single indirect use of DLLs from which you would otherwise use a large number of features.

Also as I mentioned before, not having to add the reference to Library B of course doesn't mean Library B doesn't need to be present for the program to run. You still need to have the library there, so that if and when Library A calls it, it can be used. And having the main program reference Library B is one of the easiest ways I know of to ensure that Library B is copied to the build output directory to make sure the DLL is there.


A partial class in c# is just a class that exists in multiple files. You cannot have 2 files that define a partial class in different namespaces.

0

精彩评论

暂无评论...
验证码 换一张
取 消