I just know I'm being an idiot, so somebody please tell me how.
Setup is simple:
Create a solution with three projects (.Net framework, class libraries) named InherTest, InherTest.Base, and InherTest.Base.Inherited
In InherTest.Base, add the following class:
namespace InherTest.Base
{
public abstract class BaseClass
{
internal abstract string MEMBER_1 { get; }
}
}
Copy the exact same code into InherTest, including the namespace.
In InherTest.Base.Inherited, add the following class:
namespace InherTest.Base.Inherited
{
public class Inherited : BaseClass
{
internal override string MEMBER_1 { get; }
}
}
Add a project reference in InherTest.Base.Inherited to InherTest.Base. Note the errors ("abstract member not implemented" and "no suitable member found to override").
Remove that reference and replace it with one to InherTest. Observe that the inheritance also fails.
Why don't they both work?
Edit: Originally I stated that one test would fail and the other would succeed; however, both inheritance attempts fail in the above scenario.
This is because the string is internal so limited to it's own project
Why don't they both work?
They should both fail if they contain the same code as you claim. If that is not the case then the code is different between the 2 projects, specifically the MEMBER_1 is probably declared as public in InherTest project.
The only way that a reference to InherTest would work with the same code you posted is if you have this assembly level attribute InternalsVisibleToAttribute in the project InherTest
[assembly:InternalsVisibleTo("InherTest.Base.Inherited")]
Inherited Namespaces are in different project. (Name spaces seems like together but they are not in a same assembly). You can read that article.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/internal
Related
C# solution structure
Project ApiReduced: A class library containing a file Reference.cs generated by a tool. It contains dozens of partial classes, most of which inherit from another .NET class, e.g. the class ODataService inherits from DataServiceContext. The classes are all in the same namespace Som.
Project ApiFull: Class library same as ApiReduced, but contains more partial classes, classes have further properties, etc. Think of it as a superset of ApiReduced. Again, under the same namespace Som.
Project ApiExtensions: Contains Extensions.cs, which should either (not yet decided if inheritance or extension methods make most sense)
inherit from either of the ODataService classes or
provide extension methods for the classes in either project ApiReduced or ApiFull
Project MyProject: Contains code that references either project ApiReduced or ApiFull. Furthermore, it references ApiExtensions.
Desired effect
What I want to accomplish with the above is to be able to decide in MyProject what API to use (full or reduced), depending on whether I add a reference to ApiReduced or ApiFull. In reality I have several projects, some should use the full API, some the reduced API, but I want to be able to use the same extensions in either case to avoid code duplication.
What I've tried
When in ApiExtensions I reference both ApiReduced and ApiFull, I naturally get a lot of errors that the types defined in Reference.cs exists in both libraries. On the other hand, how can I implement Extensions.cs without referencing at least one of ApiReduced or ApiFull? And if I make a choice, that seems rather arbitrary.
TL;DR
How can I swap between using two nearly identical class libraries (in the sense they define partial classes with the same names in same namespace) for my extension methods/derived class?
Short answer: Interfaces and dependency injection!
Any behaviour you want to add an extension to should be based on an interface, and then you implement that interface in both your Full & Reduced api classes. Your extensions assembly should only reference the interfaces assembly and add extensions as appropriate. At this point your assemblies look like
ApiFull (references ApiInterfaces)
ApiReduced (references ApiInterfaces)
ApiExtensions (references ApiInterfaces)
ApiInterfaces
MyProject (references ApiExtensions, ApiInterfaces and either ApiFull or ApiReduced)
Then in your project you again only code against interfaces, and in that way you can use the extensions as necessary. In this project, you use Dependency Injection to inject the correct concrete class (from either ApiFull or ApiReduced)
No answer is complete without a code sample. So, adding extensions to interfaces:
using System;
public class Program
{
public static void Main()
{
var foo = new Foo();
Console.WriteLine(foo.SayHello());
}
}
public static class INamedExtensions
{
public static string SayHello(this INamed named)
{
return $"Hello from {named.Name}";
}
}
public class Foo : INamed
{
public string Name {get => "Foo";}
}
public interface INamed
{
string Name{get;}
}
I suggest you split your Extensions into ApiReducedExtensions and ApiFullExtensions.
That way, when you want to use the Reduced API in a project you include ApiReduced and ApiReducedExtensions
And when you want to use only the FullApi, you only include ApiFull and ApiFullExtensions
Edit
To avoid duplicated code between the two you can add another class ApiSharedExtensions which contains the code both have in common. Same for the Api
So consider the case where I have a class ClassA inside of the project that is currently being generated into:
public class ClassA
{
public ClassA(int a)
{
A = a;
}
public int A { get; set; }
}
Let's say that I wanted to automatically create an extension method for ClassA, something like:
public static class ClassAExtensions
{
public static ClassA Double(this ClassA classA)
{
return new ClassA(classA.A * 2);
}
}
When trying to create this source code using the new source code generators, the compilation can't seem to find ClassA. I've tried adding the namespace of ClassA into the generated document and setting the namespace of the generated extension method class to the namespace directly to that of ClassA, but neither seem to be able to see it:
The type of namespace 'ClassA' does not exist in the namespace 'ClassANamespace' (are you missing an assembly reference?)
So the final questions are:
Is there some trick to making the code generation compiler be able to see my non-generated code?
Is this even possible right now?
Is there a workaround to get something like this to work?
Many of the samples provided declare the class being modified partial, but I don't particularly like this for what I'm trying to do.
I've also looked into adding an assembly reference, though my understanding was that the code being generated should be included and compiled alongside the existing code. Also, if this code is being compiled before my "production" code, then adding an assembly reference would not be possible and/or this would create a circular reference.
Files added in a source generator act like regular files from the perspective of the rest of the language rules so yes you can absolutely reference classes in the user's code as long as you're qualifying them correctly. It sounds like you have a bug; if there's still a specific problem you may want to try creating a project that contains both the input file and also the source generated output; you should see the same error and then can figure out what's up.
I'm writing a dll in VisualStudio. This library contains two classes with the same name but in different namespaces.
public namespace XCharting {
public class Series { }
}
public namespace ObjectModel {
public class Series { }
}
Users of my library use first one. Other one is used in other my dll (In future it is possible that users will need the second class too).
The problem is when users write "Series" in their code. IntelliSense suggest to add
1)ObjectModel namespce
2)XCharting namespace
Is is tossible to change an order of suggestions or hide suggestion of adding ObjectModel namespce?
It seems it is impossible.
I am having the following problem. I have a main project, and some extra projects that have similar functionality.
For example: I have an MVC website, then a class library project "A" with a "SettingsHelper". This just defines static wrappers for configuration settings so they can be used as propertys.
Then I have another class library project "B", which also contains a "SettingsHelper class".
How can I merge these SettingsHelpers in my main project, so I can use: SettingsHelper.Property from both modular extra projects.
I would like to be able to plug extra class libraries into one project.
Sounds pretty much like Dependency Injection. Normally you would expose SettingsHelper as an interface (your contract), and program against that. Then a DI container, such as Ninject, StructureMap, or Windsor would plug an implementation of that interface into the relevant parts of your code based on configuration.
This would allow you to code against a known contract and provide different libraries depending on the circumstances, the DI framework could then use that library to get the concrete implementation of the interface.
Would you need both instances at the same time?
Note that you cannot utilise the partial keyword across different assemblies, only within an assembly.
Update: based on your comment it sounds like you want to do something like Composition. Have a class that takes both classes from either library and combines them into one class that can be used by your application. Whether you then configure it to do something special or load the types when the libraries are present, it can all be encapsulated in this new class.
Update 2: alternatively, look into MEF:
http://msdn.microsoft.com/en-us/library/dd460648.aspx
That won't work. Partial classes cannot be divided over assemblies -- they don't exist in the CLR, only in the editor and the compiler. So they are compiled together into a single CLR class.
What you can do, is inherit one from the other. However, helpers tend to be static classes, so that won't work either.
The other alternative is not to write helper classes, but extension methods. You can extend classes in one assembly with methods defined in another assembly (or multiple other assemblies). See also http://msdn.microsoft.com/en-us/library/bb383977.aspx.
I would say that move both Helper classes in 3rd project and add reference of that project to both of your projects. So this new library will become shared datastructures and functionalities library.
Regards.
The specific pattern you are after is called a Facade Pattern. Unfortunately you are not going to get any help from the compiler getting this right. Essentially:
Create a new CombinedSettingsHelper class in your local assembly.
If the two SettingsHelper types are in the same namespace you will need to set up aliases for them (check the reference properties in the solution explorer, and MSDN documentation for this).
Implement the object so that it can access both SettingsHelper objects.
To clean up your facade you might try having a abstract method along the lines of abstract object GetSettingValue(string name);. Your facade could then inherit from the same base class and call these on its contained children. For example:
public abstract class SettingsHelperBase { public object GetSettingValue(string settingName); }
// Assembly1
public class SettingsHelper : SettingsHelperBase { }
// Assembly2
public class SettingsHelper : SettingsHelperBase { }
public class SettingsHelper : SettingsHelperBase
{
private List<SettingsHelperBase> _backends = new List<SettingsHelperBase>();
public readonly PropertiesImpl Properties;
public class PropertiesImpl
{
private SettingsHelper _settingsHelper;
public string Name
{
get
{
return (string)_settingsHelper.GetSettingValue("Name");
}
}
internal PropertiesImpl(SettingsHelper helper)
{
_settingsHelper = helper;
}
}
public SettingsHelper()
{
_backends.Add(asm1::MyNs.SettingsHelper);
_backends.Add(asm2::MyNs.SettingsHelper);
Properties = new PropertiesImpl(this);
}
protected override object GetSettingValue(string settingName)
{
foreach (var item in _backends)
{
var val = item.GetSettingValue(settingName);
if (val != null)
return val;
}
return null;
}
}
There is a way; Visual Studio allows the same code file to be included in more than one project.
When you do “Add”/”Existing Item” to can select a file that is in the different folder.
This is what some of the silver light support does so as to allow a “common class” that has some method that are only on the server and one methods that are only on the client.
(As to the question of “good design” you will have to decide that yourself, a lot of people don’t like having the same class compiled in different ways in different projects. Think if the mess you could get in with #if XXX, when XXX is only defined in one of the projects)
Say I have an old class, dated from c# 1.1 and I would like to add more methods, fields to it. Now I am using 2005, and thus the most logical solution is to use partial classes. However, it seems that I have to pretend word partial to every class in a file where I define it.
The problem:
What if I cannot change the class declaration in an old file (add word partial to it), but still want to add methods to it, what should I do?
Well, firstly, yes you do need to use the partial keyword to all of the involved classes, under the same namespace. This will tell the compiler that those are the parts of the same class that will be put together.
Now, if you really cannot change the old classes, one thing you can do is to inherit your old class:
public class NewClass : OldClass
...and as such you can extend the functionality of the OldClass.
You may also choose to just consume the old class some sort of wrapper, as an attribute/property:
public class NewClass
{
public OldClass MyClass { get; set; } //.NET 3.5 / VS2008
private OldClass _oldClass; //.NET 2.0 / VS2005
public OldClass MyClass
{
get { return _oldClass; }
set { _oldClass = value; }
}
}
...or even a generic:
public class NewClass<T> where T: OldClass //applicable in both versions
The suggestion for extension methods will also work:
public void NewMethod1(this OldClass, string someParameter){} //available only in .NET 3.5/VS2008
You should derive from it.
Well, there's always extension methods.
Here is my order:
Does extension method available for
you? If you are on VS2005, that may
be not.
Then, you probably have to inherit
from that class to add more methods.
If the class is sealed, then you
need wrap your class into another
class with "composition patter".
You cannot do anything. You are not allowed to edit the old file, so you cannot add members to this file. Also you cannot add the partial keyword and add the members in another file.
The only ways out might be to write an extension methods (requires C# 3.0) or to create a derived class. Both solution do not (really) modify the original class.
If you're using VS2005 and you cannot modify the old class then really there is nothing you can do. If you were using VS2008 you could use extension methods to "simulate" adding new classes but nothing in 2005 will help you.
You're only possible option is really to subclass if possible and add new methods that way. Probably not going to fix the problem you're having though.
Why can you not change the old file? That seems fairly extreme.