c# Can I prevent my constructor arguments clashing with VBA instantiation? - c#

I'm a bit confused about how I can override constructors requiring arguments when using their classes in a VBA environment.
What works?
I've created several classes in a library, each with an interface to allow full intellisense compatibility when using this library in VBA
With or without constructors, these classes work fine for me e.g.
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("JamHeadArt.ClassEX")]
[Guid("XYZ")]
public partial class ClassEX : IClassEX
{
public ClassEX()
{
// Empty constructor here, some of mine have processes, all work well
}
// Methods/ Properties as outlined by the interface below
}
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("ABC")]
public interface IClassEx
{
// Various methods / fields / properties to be implemented by ClassEX
}
I then add reference to my library and write simple lines of code in VBA to instantiate and access my classes:
Sub Test()
Dim t As JamHeadArt.ClassEX
Set t = New JamHeadArt.ClassEX
' Using t.dot then provides all the methods needed '
End Sub
What goes wrong?
When I create contstructors with arguments (even if optional) in a class, VBA will stop allowing me to create instances of these classes, it tells me the "New" keyword is Invalid and actually won't allow me to choose the class from the intellisense list of objects in my library if I go straight for Dim t As New JamHeadArt.ClassEx even if the parameters are set to optional (therefore not really needed)
The annoying thing here is - I don't actually want my VBA instances to accept parameters via the constructor, they're mainly there for Unit testing and they're optional strings so default to "" ... so I guess my question is something like
Is it possible to override any constructor parameters so when referenced in a VBA environment it will ignore them?
e.g. I really want my constructor to look like this:
public ClassEX(string s = "")
{
}
and in VBA it should work as before Dim t As New JamHeadArt.ClassEX - but it won't with that optional string in there!

You can add an additional constructor, for example:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("JamHeadArt.ClassEX")]
[Guid("XYZ")]
public partial class ClassEX : IClassEX
{
public ClassEX()
{
// Empty constructor here, some of mine have processes, all work well
}
public ClassEX(string foo)
{
// additional constructor, can be used for unit testing etc.
}
// Methods/ Properties as outlined by the interface below
}

Related

Using constructor arguments with NinjectModule

I have the following scenario for declaring export plugins for my application:
public abstract class PluginBase : NinjectModule
{
protected PluginBase(IDataSource source)
{
// ...
}
public override void Load()
{
Bind<PluginBase>().To(GetType());
}
}
public class RealPlugin : PluginBase
{
public RealPlugin(IDataSource source)
{
// ...
}
}
Unfortunatelly, Kernel.Load(AssemblyName) doesn't seem to detect RealPlugin as loadable element and the Load() method is never called. It works if I add an extra public constructor with no arguments to both classes. However, I actually don't want to do that because I don't want anyone to create an instance of RealPlugin without specifying the data source.
An ugly workaround seems to be to mark the parameterless constructors with [Obsolete], which at least prevents accidental usage of them.
Of course, I could also create separate classes deriving from NinjectModule to create the Bindings, but that requires another class for each of my Plugins, which is also not so nice (and prevents binding to the dynamic type of the instance, as seen above)
Anyone got an idea how such a plugin can be registered without having a public parameterless constructor?
In Reflection, by default, only "public" types are considered. Apparently ninject uses the default values. So you'll have to roll your own implementation which uses reflection to find all classes which match all of these criteria:
inheriting from NinjectModule (also indirectly)
not abstract
has a parameter-less constructor
then these need to be instanciated and passed on to Kernel.Load(NinjectModule).
You could, just as well, look at the source code of Kernel.Load(AssemblyName) and copy and modify it according to your needs. Most likely the code making use of reflection will just require a false switched to a true in one or two places - or using a different method overload which considers non-public members, too.

.NET vb6 wrapper property with parameter

i need to write a c# wrapper for a vb6 application. I always get error 450 ( Wrong number of arguments or property assignment was not valid.) This is my VB Code
Dim DBEngine As New DBEngineNet
Set mDbEProp = DBEngine.Properties("Version") ' <-- ERROR
This code is working, so the problem is the parameter of the property
Dim DBEngine As New DBEngineNet
Set mDbEProps = DBEngine.Properties
Set mDbEProp = mDbEProps("Version") '<-- Working. Results 1.0
Here is my COM-Visible C#-Code. It uses the Interop-Interfaces of the old VB6-MotorApp.
[ComVisible(true)]
public class DBEngineNet : VB6MotorApp.DBEngine
{
public VB6MotorApp.Properties Properties
{
// [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_SAFEARRAY)] Maybe something like this???
get
{
return new PropertiesNet
{
new PropertyNet{Name="Version", Value="1.0"}
};
}
}
Here is the Properties-Object:
[ComVisible(true)]
public class PropertiesNet : VB6MotorApp.Properties, IList<PropertyNet>
{
List<PropertyNet> _properties = new List<PropertyNet>();
public VB6MotorApp.Property this[object Item]
{
get
{
return _properties.FirstOrDefault(p => p.Name == Item.ToString());
}
}
}
Any ideas?
The basic diagnostic tool you need here is OleView.exe, run it from the Visual Studio Command Prompt. Use its File + View typelib command to look at the type libraries and compare them. First on your original VB6 implementation so you have a base-line, next on the type library for your .NET version.
There are inevitably going to be major difference the way you are doing it now, you are exposing too many details of the class implementation. All of the System.Object methods as well as the IList<> implementation methods are going to be visible. Boilerplate is to declare a [ComVisible(true)] interface (VB6 likes their name to start with an _underscore) and hide the class implementation by giving it the [ClassInterface(ClassInterfaceType.None)] attribute. You already have the interface so only the attribute should be necessary.
What you want to look for first in the OleView.exe output is the [dispid] attribute for the DBEngineNet.Properties property. It doesn't act like the default property which is why you have to obtain the property value explicitly in your VB6 code. The default property has dispid(0). You force the value in .NET code by giving it the [DispId(0)] attribute.
You also want to look at the original type library, "VB6MotorApp.Properties" looks wrong. That's a coclass name, not an interface name. Non-zero odds that you should be using VB6MotorApp._Properties. Same for VB6MotorApp._DBEngine.
And look at which interfaces in the coclasses have the [default] attribute. It should be the VB6 interfaces. Probably not an issue if your VB6 snippets work as posted.

Passing objects from C# to VBA using COM Interop

Is it possible to pass a custom object (like MyClass[]) from C# to VBA using COM?
If not, which is the best solution to get this working?
I assume you're talking about Excel VBA to C# ...
here's a minimal C# class that does it, in a project w default name ClassLibrary1:
using System;
using System.Runtime.InteropServices;
namespace Tester
{
[ClassInterface(ClassInterfaceType.AutoDual)]
public class TestClass
{
public double D { get; set; } // simple property to get, set a double
public string S { get; set; } // simple property to get, set a string
}
}
and here's VBA to try the class out:
Private Sub foo()
Dim X As New ClassLibrary1.TestClass
X.S = "Hello"
Debug.Print X.S ' prints "hello"
X.D = 12
Debug.Print X.D ' prints a 12
End Sub
and here are the extra things you need to do to make this work:
(1) in C# Project...Properties...Build ==> check "Register for COM interop
(2) in C# Project...Properties...Application...Assembly Information ==>
check "Make assembly COM-visible"
(3) in VBA ... Tools ... References, browse to the C# bin output directory and select the "*.tlb" file
Note: this scheme may fail depending on what you add to the class - I don't think VBA will "see" static classes or classes w other than default constructors. You also cannot map VB collections to .NET collections, but you will be able to pass basic types (double, long) and arrays of the basic types back and forth. Also - the "Autodual" option used is a cheap way to get methods exposed ... easy to get started but less efficient and exposes all public methods. Better practice (but more work) would be to set up your own interfaces. If you expand the members of this TestClass to include instances of other classes you have defined, and if you likewise expose those class methods via AutoDual or via hand-coded interfaces, then those classes and their (non-overloaded) methods will likewise be visible in VBA (with Intellisense).
Hope this helps.

VB.NET cross compatibility with C#

I have a C# project with a generic interface
public interface IMyFoo<T> { void DoSomething(T instance); }
I also have a C# project with an interface that inherits from several IMyFoos
public interface IMyBar : IMyFoo<Type1>, IMyFoo<Type2> { ... }
Everything works fine in C# land (including the scenario below which doesn't work in VB).
I have a VB .NET project that references this C# library.
I have an instance of IMyBar and try to use as follows:
Dim instance as MyType1 = ...
Dim bar as IMyBar = ...
bar.DoSomething(instance) ' This generates a compile error:
' 'DoSomething' is ambiguous across the inherited interfaces 'IMyFoo(Of MyType1)' and 'IMyFoo(Of MyType2)'.
What's up? I can DirectCast like this and it works fine...but I'd REALLY rather not
DirectCast(bar, IMyFoo(Of MyType1)).DoSomething(instance)
You're probably going to have to cast:
Unlike other types, which only derive from a single base type, an interface may derive from multiple base interfaces. Because of this, an interface can inherit an identically named type member from different base interfaces. In such a case, the multiply-inherited name is not available in the derived interface, and referring to any of those type members through the derived interface causes a compile-time error, regardless of signatures or overloading. Instead, conflicting type members must be referenced through a base interface name.
Just to go into further details, the reason VB doesn't directly support what you are doing is because what you showed is just a special case of a class implementing two interfaces with the same method. Whenever VB sees this it forces a cast to make it explicit which one you are intending to use. The VB designers decided that this would make code less error-prone. C# goes further and assumes that you know what you are doing and lets you make that call. You can make C# get the same basic error by using the more generic case of two interfaces with the same method:
public interface IMyFoo1 { void DoSomething(string instance); }
public interface IMyFoo2 { void DoSomething(string instance); }
public interface IMyBar : IMyFoo1, IMyFoo2 { }
public class MyTestClass : IMyBar
{
//Explicit interface declaration required
void IMyFoo1.DoSomething(string instance) { }
void IMyFoo2.DoSomething(string instance) { }
}
string s = "";
IMyBar bar = new MyTestClass();
bar.DoSomething(s);//The call is ambiguous between the following methods or properties...

C# share code between classes

In Visual Studio 2008 using C#, what is the best way to share code across multiple classes and source files?
Inheritance is not the solution as the classes already have a meaningful hierarchy.
Is there some neat feature that's like a C include file that let's you insert code anywhere you want in another class?
EDIT:
ok, i guess we need a concrete example...
There are several hundred classes in the domain with a well thought out class heirarchy. Now, many of these classes need to print. There is a utility printer class that handles the printing. Let's say there are 3 different print methods that are dependent on the class that is being printed. The code that calls the print method (6 lines) is what I'm trying to avoid copying and pasting across all the different client class pages.
It'd be nice if people wouldn't assume they knew more about the domain that the op - especially when they specifically mention techniques that don't fit...
If you have functionality that you use frequently in classes that represent very different things, in my experience that should fall into just a few categories:
Utilities (e.g. string formatting, parsing, ...)
Cross-cutting concerns (logging, security enforcement, ...)
For utility-type functionality you should consider creating separate classes, and referencing the utility classes where needed in the business class.
public class Validator
{
public bool IsValidName(string name);
}
class Patient
{
private Validator validator = new Validator();
public string FirstName
{
set
{
if (validator.IsValidName(value)) ... else ...
}
}
}
For cross-cutting concerns such as logging or security, I suggest you investigate Aspect-Oriented Programming.
Regarding the PrintA vs. PrintB example discussed in other comments, it sounds like an excellent case for the Factory Pattern. You define an interface e.g. IPrint, classes PrintA and PrintB that both implement IPrint, and assign an instance of IPrint based on what the particular page needs.
// Simplified example to explain:
public interface IPrint
{
public void Print(string);
}
public class PrintA : IPrint
{
public void Print(string input)
{ ... format as desired for A ... }
}
public class PrintB : IPrint
{
public void Print(string input)
{ ... format as desired for B ... }
}
class MyPage
{
IPrint printer;
public class MyPage(bool usePrintA)
{
if (usePrintA) printer = new PrintA(); else printer = new PrintB();
}
public PrintThePage()
{
printer.Print(thePageText);
}
}
You can't just load in code that you'd like to have added into a class in C# via a preprocessor directive like you would in C.
You could, however, define an interface and declare extension methods for that interface. The interface could then be implemented by your classes, and you can call the extension methods on those classes. E.g.
public interface IShareFunctionality { }
public static class Extensions
{
public static bool DoSomething(this IShareFunctionality input)
{
return input == null;
}
}
public class MyClass : Object, IShareFunctionality
{
public void SomeMethod()
{
if(this.DoSomething())
throw new Exception("Impossible!");
}
}
This would allow you to reuse functionality, but you cannot access the private members of the class like you would be able to if you could, say, hash include a file.
We might need some more concrete examples of what you want to do though?
A C# utility class will work. It acts like a central registry for common code (or like the VB.NET Module construct) - it should contain code that's not specific to any class otherwise it should have been attached to the relevant class.
You don't want to start copying source code around if you don't have to because that would lead to code update problems considering the duplication.
As long as the source doesn't need to retain state, then use a static class with static method.
static public class MySharedMembers {
static public string ConvertToInvariantCase(string str) {
//...logic
}
// .... other members
}
If the classes are in the same namespace, there's no need for an include analog. Simply call the members of the class defined in the other function.
If they're not in the same namespace, add the namespace of the classes you want to use in the usings directives and it should work the same as above.
I'm confused by the question: it seems you need to work on your basic OO understanding.
Checkout extension methods: http://msdn.microsoft.com/en-us/library/bb383977.aspx
I don't know of a way to include portions of files but one thing we do frequently is to add an existing file and "link" it from its current location. For example, we have an assemblyInfo.cs file that every project refers to from a solution directory. We change it once and all the projects have the same info because they're referring to the same file.
Otherwise, suggestions about refactoring "common" routines in a common.dll are the best thing I've come up with in .Net.
I am not sure exactly what you mean by a "meaningful" structure already, but this sounds like a place where you could use base class implementation. Though not as "verbose" as C++ multiple inheritance, you might get some benefit out of using chained base class implementation to reuse common functions.
You can preserve class hierarchy, at least visually and override behavior as needed.
Pull out the repetitive code into services. The repetitive code is a clue that there might be some room for refactoring.
For example, create a "PrintingService" which contains the logic needed to print. You can then have the classes that need to print have a dependency on this service (either via the constructor or a parameter in a method which requires the service).
Another tip i have along these lines is to create interfaces for base functionality and then use the interfaces to code against. For example, i had bunch of report classes which the user could either fax, email, or print. Instead of creating methods for each, i created a service for each, had them implement an interface that had a single method of Output(). I could then pass each service to the same method depending on what kind of output the user wanted. When the customer wanted to use eFax instead of faxing through the modem, it was just a matter of writing a new service that implemented this same interface.
To be honest I can't think of anything like includes in Visual C#, nor why you would want that feature. That said, partial classes can do something like it sounds what you want, but using them maybe clashes against your "classes already have a meaningful hierarchy" requirement.
You have many options, TT, extension method, delegate, and lambda

Categories

Resources