Running C#/.NET code from Cloud 9 IDE
I was experimenting with Cloud 9 IDE mainly for remote coding interviews.
I also wanted to see if I could add C# support. C# syntax highlighting was already supported in Cloud 9. Mono is also pre installed in the Cloud 9 Environment.
All I had to do was add a custom runner to use mcs command line compiler for mono.
I am a complete newbie when it comes to ubuntu / bash etc., so please bear with if there are any script issues with the runner.
You can configure a custom runner using this json:
{ "cmd" : [ "bash", "--login", "-c", "mcs '*.cs' -out:'$project_path$project_name.exe' $args;mono '$project_path$project_name.exe' $args" ], "info" : "Started $project_path$project_name", "env" : {}, "selector" : "source.cs" }
Delegates
Imagine we are writing an Employees Collection class which has methods to filter by first name and department. The code looks so similar, can we somehow parameterize it to remove the duplication?
using System; using System.Collections.Generic; namespace CodingArchitect.Demos.Delegates { public class EmployeesCollection { private readonly List<Employee> employees = new List<Employee>(); public EmployeesCollection FilterByFirstName(string firstName) { var matches = new EmployeesCollection(); foreach (var employee in employees) { if(string.Equals(employee.FirstName, firstName, StringComparison.InvariantCulture)) { matches.Add(employee); } } return matches; } public EmployeesCollection FilterByDepartment(string department) { var matches = new EmployeesCollection(); foreach (var employee in employees) { if (string.Equals(employee.Department, department, StringComparison.InvariantCulture)) { matches.Add(employee); } } return matches; } private void Add(Employee employee) { employees.Add(employee); } } public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public string Department { get; set; } } }
Parameterize Method refactoring immediately comes to mind, only in this case the parameter should be a function which does the actual filtering. Passing functions as parameters is perfectly valid in C#. Now the condition alone can be passed as a delegate to the filter method. I am reusing the Predicate<T> delegate defined in the BCL. This is exactly what the FindAll method on the List<T> type in BCL does.
using System; using System.Collections.Generic; namespace CodingArchitect.Demos.Delegates { public class EmployeesCollection { private readonly List<Employee> employees = new List<Employee>(); public EmployeesCollection Filter(Predicate<Employee> condition) { var matches = new EmployeesCollection(); foreach (var employee in employees) { if (condition(employee)) { matches.Add(employee); } } return matches; } private void Add(Employee employee) { employees.Add(employee); } } public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public string Department { get; set; } } }
The condition predicate can now be passed from the call site using a delegate (syntactic sugar like anonymous delegates, lambda expressions make it easier further).
Mike is doing a session @ the Orlando Code Camp
If you are a .NET Developer living in/near Orlando, do not miss this session in Orlando Code Camp.
http://www.orlandocodecamp.com/Agenda.aspx/SessionDetail/5e6dc9ed-e2fc-4e2b-974b-b2cf6a800c17
Having worked with Mike for 3 years on the Model Driven Code Generation, I highly recommend it.
Embedded Web Resources related Javascript errors
It is sort of a long rant, feel free to jump to the end if you want to read the findings alone.
We had been running into this issue for more than an year now. I have been postponing getting to the bottom of the issue because we found some solution which works. We did not know why. A healthy prod by my client forced me research on the issue further.
The problem: Javascripts from embedded resources within assemblies were not getting downloaded and we used to get errors like ‘YAHOO’ is undefined, ‘xyz..’ is undefined etc. Initially I was thinking that the assembly has gone corrupt during the file transfer and used to retransfer the assembly to the server. And it used to work. Sometimes even a retransfer of file doesn’t work. After that we tried using reflector on the so called ‘corrupt’ assembly, but it opened like a charm indicating the file hasn’t gone corrupt. We also used to zip the files and when we transfer the files without zipping things used to work.
We thought the problem was with the zipping tool used to create the zip file and tried different options but in vain. A uncompressed file transferred directly used to work where as a zipped file fails, even though it opens fine in reflector. After some frantic searching yesterday I found the real reason. Initially I was on a wild goose chase around the lines of “The not so mysterious problem around webresource.axd”. I also created a sample to simulate the issue, but it vain.
Today morning I ran into “Web Resources Troubleshooting” from the Telerik folks. After reading the article I realized I had seen the error earlier in one of the dev boxes due to the date being in the future. I created a small sample to reproduce the issue and bingo! But it did not answer the question how retransfer was fixing the issues. We always zip the files and transfer the website. When 1 or 2 odd dlls go missing we do not zip that during the transfer.
The source and destination machines were in different time zones / times. The following conditions had to be true to get a the exception.
- If the assembly build time happens to be within the time difference
- and you zip the assembly for transferring
- and for unzipping you use Windows Explorer Compressed folder feature
the issue will occur
I did some more research and found the following facts.
Findings: Different applications use different philosophies when it comes to handling datetime stamps (Date Created & Date Modified). I tried the following applications
- Windows explorer copy & paste to destination folder (from an ftp site).
- Internet explorer saving the file from a web site
- Windows Explorer Compressed folder feature to extract files.
- 7Zip Extract files feature
The Windows Explorer Compressed folder extract files uses the timestamp of the source machine as is where as the other scenarios the local machine’s timestamp was used (I am overly simplifying it, you can try this on your own).
You can use Fiddler / firebug to see if any webresource / scriptresource are not getting downloaded. Here is the original error.
"Specified argument was out of the range of valid values. Parameter name: utcDate"
The assembly containing the embedded resources is probably built in the future (its last modified time is later than the current time). This can occur when deploying in a different time zone. In such case run the following command line statement (the commas and plus at the end are important!):
copy /b <path to assembly which is built in the future>+,,”
A little reflectoring lead me to AssemblyResourceLoader.GetAssemblyInfoWithAssertInternal which was using File.GetLastWriteTime rather than GetLastWriteTimeUtc. Is this the cause for the exception? I didn’t look deep enough to figure that.
Linq to SQL Detached IQueryable / Disconnected Queries / Detached Criteria
It has been a long time since I wrote something on technology. No, I am not becoming a non technical manager. It was just that I was lazy to write something up.
We are planning to standardize on LINQ’s IQueryable as the Query Object in our project. We had to choose between LINQ to SQL vs LINQ to Entities. Since LINQ to Entities / Entity Framework is still prerelease stuff we decided to check with LINQ to SQL first. A few issues which made us think before adopting LIINQ to SQL
- No support for Disconnected Query Building (akin to NHibernate’s Detached Criteria)
- No support for explicit detach of objects & object graphs from the DataContext (akin to NHibernate Detached entities / objects). You need to Serialize De-Serialize to detach an Entity Instance. Combine this with the WCF & LINQ Serialization issues with object graphs situation become further worse.
- No support for attaching detached (detached by serialization / deserialization) object graphs.
- IQueryable implementations & Expression classes are not serializable.
- LINQ to SQL supports only Single Table Inheritance
But still LINQ to SQL query syntax is really cool. So we decided to use LINQ just for Querying and let our own framework handle the persistence mapping.
All of these limitations made me think of LINQ to SQL is just a cool Toy which cannot be used in production quality applications. In fact even some Microsoft folks have said something along these lines "LINQ to SQL supports rapid development of applications that query Microsoft SQL Server databases using objects that map directly to SQL Server schemas. LINQ to Entities supports more flexible mapping of objects to Microsoft SQL Server and other relational databases through extended ADO.NET Data Providers". From http://blogs.msdn.com/data/archive/2007/04/28/microsoft-s-data-access-strategy.aspx. LINQ to SQL is not for complex mapping scenarios. For complex Mapping scenarios use LINQ to Entities (it looks like L2E is going to bring in its own additional complexity).
How do we do disconnected LINQ queries which are going to be executed later by the LINQ to SQL provider / How do we simulate Detached Criteria using LINQ / Is there a Detached IQueryable implementation?
LINQ to SQL doesn’t support disconnected queries natively. As a workaround we were tempted to try IEnumerable.AsQueryable() which returns a EnumerableQuery on the client side (I know purists must be raising their eyebrows now that this should be done on the server side, in reality you cannot think all possible scenarios and let the client send in queries for scenarios that one hasn’t thought about). On the server side when we called DataContext.GetCommand(queryable) it started returning a SqlCommand with SELECT NULL AS [EMPTY] as CommandText.
Next we tried to create a disconnected query using a DataContext object initialized with an empty connection string on the client side. On the server side when we called DataContext.GetCommand(queryable) it started throwing an exception stating ‘The query contains references to items defined on a different data context’. Looks like LINQ to SQL wasn’t happy about a disconnected context no matter what you did.
We decided to use InterLinq‘s ExpressionConverter (actually it’s a slightly modified version of InterLinq’s ExpressionConverter inspired by Matt Waren‘s ExpressionVisitor) to rewrite the IQueryable references in the expression tree with the IQueryable created on the server side . We started getting another exception that Parameter ‘e’ not in scope. It looked like Matt’s code didn’t use the parameters collection returned by visit call inside the VisitLambda handler. Once that was fixed we were good to go.
public class CloningExpressionVisitor { private readonly IQueryBinder queryBinder; private Dictionary<int, object> m_convertedObjects = new Dictionary<int, object>(); private readonly Expression expressionToConvert; public CloningExpressionVisitor(Expression expression, IQueryBinder queryBinder) { this.queryBinder = queryBinder; expressionToConvert = expression; } public object Visit() { m_convertedObjects = new Dictionary<int, object>(); return VisitResult(expressionToConvert); } /// <summary> /// Returns the value of the <see cref="Expression"/>. /// </summary> /// <param name="expression"><see cref="Expression"/> to visit.</param> /// <returns>Returns the value of the <see cref="Expression"/>.</returns> protected virtual object VisitResult(Expression expression) { if (expression == null) { return null; } if (m_convertedObjects.ContainsKey(expression.GetHashCode())) { return m_convertedObjects[expression.GetHashCode()]; } object foundObject = null; if (expression is ConstantExpression) { foundObject = GetResultConstantExpression((ConstantExpression)expression); } else if (expression is MethodCallExpression) { foundObject = GetResultMethodCallExpression((MethodCallExpression)expression); } else { throw new NotImplementedException(); } m_convertedObjects[expression.GetHashCode()] = foundObject; return foundObject; } protected virtual object GetResultConstantExpression(ConstantExpression expression) { object value = expression.Value; if (value == null) { return null; } else if (value is IQueryable) { return queryBinder.GetQueryBoundToNewProvider((value as IQueryable).ElementType); } return value; } protected virtual object GetResultMethodCallExpression( MethodCallExpression expression ) { return InvokeMethodCall( expression ); } protected object InvokeMethodCall( MethodCallExpression ex ) { if( ex.Method.DeclaringType == typeof( Queryable ) ) { var args = new List<object>(); for( int i = 0; i < ex.Arguments.Count; i++ ) { Expression currentArg = ex.Arguments[i]; if( typeof( Expression ).IsAssignableFrom( currentArg.Type ) ) { args.Add( ( (UnaryExpression) Visit( currentArg ) ).Operand ); } else { args.Add( VisitResult( currentArg ) ); } } return ex.Method.Invoke( ex.Object, args.ToArray() ); } // If the method is not of DeclaringType "Queryable", it mustn't be invoked. // Without this check, we were able to delete files from the server disk // using System.IO.File.Delete( ... )! throw new SecurityException( string.Format( "Could not call method '{0}' of type '{1}'. Type must be Queryable.", ex.Method.Name, ex.Method.DeclaringType.Name ) ); } public virtual Expression Visit(Expression exp) { if (exp == null) return exp; if (m_convertedObjects.ContainsKey(exp.GetHashCode())) { return (Expression)m_convertedObjects[exp.GetHashCode()]; } Expression returnValue = null; switch (exp.NodeType) { case ExpressionType.Negate: case ExpressionType.NegateChecked: case ExpressionType.Not: case ExpressionType.Convert: case ExpressionType.ConvertChecked: case ExpressionType.ArrayLength: case ExpressionType.Quote: case ExpressionType.TypeAs: case ExpressionType.UnaryPlus: returnValue = VisitUnary((UnaryExpression)exp); break; case ExpressionType.Add: case ExpressionType.AddChecked: case ExpressionType.Subtract: case ExpressionType.SubtractChecked: case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.Divide: case ExpressionType.Modulo: case ExpressionType.And: case ExpressionType.AndAlso: case ExpressionType.Or: case ExpressionType.OrElse: case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: case ExpressionType.Equal: case ExpressionType.NotEqual: case ExpressionType.Coalesce: case ExpressionType.ArrayIndex: case ExpressionType.RightShift: case ExpressionType.LeftShift: case ExpressionType.ExclusiveOr: case ExpressionType.Power: returnValue = VisitBinary((BinaryExpression)exp); break; case ExpressionType.TypeIs: returnValue = VisitTypeIs((TypeBinaryExpression)exp); break; case ExpressionType.Conditional: returnValue = VisitConditional((ConditionalExpression)exp); break; case ExpressionType.Constant: returnValue = VisitConstant((ConstantExpression)exp); break; case ExpressionType.Parameter: returnValue = VisitParameter((ParameterExpression)exp); break; case ExpressionType.MemberAccess: returnValue = VisitMemberAccess((MemberExpression)exp); break; case ExpressionType.Call: returnValue = VisitMethodCall((MethodCallExpression)exp); break; case ExpressionType.Lambda: returnValue = VisitLambda((LambdaExpression)exp); break; case ExpressionType.New: returnValue = VisitNew((NewExpression)exp); break; case ExpressionType.NewArrayInit: case ExpressionType.NewArrayBounds: returnValue = VisitNewArray((NewArrayExpression)exp); break; case ExpressionType.Invoke: returnValue = VisitInvocation((InvocationExpression)exp); break; case ExpressionType.MemberInit: returnValue = VisitMemberInit((MemberInitExpression)exp); break; case ExpressionType.ListInit: returnValue = VisitListInit((ListInitExpression)exp); break; default: throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType)); } m_convertedObjects.Add(exp.GetHashCode(), returnValue); return returnValue; } protected virtual MemberBinding VisitBinding(MemberBinding binding) { switch (binding.BindingType) { case MemberBindingType.Assignment: return this.VisitMemberAssignment((MemberAssignment)binding); case MemberBindingType.MemberBinding: return this.VisitMemberMemberBinding((MemberMemberBinding)binding); case MemberBindingType.ListBinding: return this.VisitMemberListBinding((MemberListBinding)binding); default: throw new Exception(string.Format("Unhandled binding type '{0}'", binding.BindingType)); } } protected virtual ElementInit VisitElementInitializer(ElementInit initializer) { ReadOnlyCollection<Expression> arguments = this.VisitExpressionList(initializer.Arguments); return Expression.ElementInit(initializer.AddMethod, arguments); } protected virtual Expression VisitUnary(UnaryExpression u) { Expression operand = this.Visit(u.Operand); return Expression.MakeUnary(u.NodeType, operand, u.Type, u.Method); } protected virtual Expression VisitBinary(BinaryExpression b) { Expression left = this.Visit(b.Left); Expression right = this.Visit(b.Right); Expression conversion = this.Visit(b.Conversion); if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null) return Expression.Coalesce(left, right, conversion as LambdaExpression); else return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method); } protected virtual Expression VisitTypeIs(TypeBinaryExpression b) { Expression expr = this.Visit(b.Expression); return Expression.TypeIs(expr, b.TypeOperand); } protected virtual Expression VisitConstant(ConstantExpression expression) { if (expression.Value == null) { return null; } return Expression.Constant(expression.Value); } protected virtual Expression VisitConditional(ConditionalExpression c) { Expression test = this.Visit(c.Test); Expression ifTrue = this.Visit(c.IfTrue); Expression ifFalse = this.Visit(c.IfFalse); return Expression.Condition(test, ifTrue, ifFalse); } protected virtual Expression VisitParameter(ParameterExpression p) { return Expression.Parameter(p.Type, p.Name); } protected virtual Expression VisitMemberAccess(MemberExpression m) { Expression exp = this.Visit(m.Expression); return Expression.MakeMemberAccess(exp, m.Member); } protected virtual Expression VisitMethodCall(MethodCallExpression m) { Expression obj = this.Visit(m.Object); IEnumerable<Expression> args = this.VisitExpressionList(m.Arguments); //switch (m.Method.Name) //{ // case "Where": // args = new List<Expression>() { Expression.Constant(queryable, queryable.GetType()), this.Visit(m.Arguments[1]) }; // break; //} return Expression.Call(obj, m.Method, args); } protected virtual ReadOnlyCollection<Expression> VisitExpressionList(ReadOnlyCollection<Expression> original) { List<Expression> list = null; for (int i = 0, n = original.Count; i < n; i++) { Expression p = this.Visit(original[i]); if (list != null) { list.Add(p); } else if (p != original[i]) { list = new List<Expression>(n); for (int j = 0; j < i; j++) { list.Add(original[j]); } list.Add(p); } } if (list != null) { return list.AsReadOnly(); } return original; } protected virtual MemberAssignment VisitMemberAssignment(MemberAssignment assignment) { Expression e = this.Visit(assignment.Expression); return Expression.Bind(assignment.Member, e); } protected virtual MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { IEnumerable<MemberBinding> bindings = this.VisitBindingList(binding.Bindings); return Expression.MemberBind(binding.Member, bindings); } protected virtual MemberListBinding VisitMemberListBinding(MemberListBinding binding) { IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(binding.Initializers); return Expression.ListBind(binding.Member, initializers); } protected virtual IEnumerable<MemberBinding> VisitBindingList(ReadOnlyCollection<MemberBinding> original) { List<MemberBinding> list = null; for (int i = 0, n = original.Count; i < n; i++) { MemberBinding b = this.VisitBinding(original[i]); if (list != null) { list.Add(b); } else if (b != original[i]) { list = new List<MemberBinding>(n); for (int j = 0; j < i; j++) { list.Add(original[j]); } list.Add(b); } } if (list != null) return list; return original; } protected virtual IEnumerable<ElementInit> VisitElementInitializerList(ReadOnlyCollection<ElementInit> original) { List<ElementInit> list = null; for (int i = 0, n = original.Count; i < n; i++) { ElementInit init = this.VisitElementInitializer(original[i]); if (list != null) { list.Add(init); } else if (init != original[i]) { list = new List<ElementInit>(n); for (int j = 0; j < i; j++) { list.Add(original[j]); } list.Add(init); } } if (list != null) return list; return original; } protected virtual Expression VisitLambda(LambdaExpression lambda) { Expression body = this.Visit(lambda.Body); IEnumerable<ParameterExpression> parameters = VisitCollection<ParameterExpression>(lambda.Parameters); return Expression.Lambda(lambda.Type, body, parameters); } public IEnumerable<T> VisitCollection<T>(IEnumerable enumerable) where T : Expression { if (enumerable == null) { return null; } var returnValues = new List<T>(); foreach (Expression expression in enumerable) { returnValues.Add((T)Visit(expression)); } return returnValues; } protected virtual NewExpression VisitNew(NewExpression nex) { IEnumerable<Expression> args = this.VisitExpressionList(nex.Arguments); if (nex.Members != null) return Expression.New(nex.Constructor, args, nex.Members); else return Expression.New(nex.Constructor, args); } protected virtual Expression VisitMemberInit(MemberInitExpression init) { NewExpression n = this.VisitNew(init.NewExpression); IEnumerable<MemberBinding> bindings = this.VisitBindingList(init.Bindings); return Expression.MemberInit(n, bindings); } protected virtual Expression VisitListInit(ListInitExpression init) { NewExpression n = this.VisitNew(init.NewExpression); IEnumerable<ElementInit> initializers = this.VisitElementInitializerList(init.Initializers); return Expression.ListInit(n, initializers); } protected virtual Expression VisitNewArray(NewArrayExpression na) { IEnumerable<Expression> exprs = this.VisitExpressionList(na.Expressions); if (na.NodeType == ExpressionType.NewArrayInit) { return Expression.NewArrayInit(na.Type.GetElementType(), exprs); } else { return Expression.NewArrayBounds(na.Type.GetElementType(), exprs); } } protected virtual Expression VisitInvocation(InvocationExpression iv) { IEnumerable<Expression> args = this.VisitExpressionList(iv.Arguments); Expression expr = this.Visit(iv.Expression); return Expression.Invoke(expr, args); }
}
public class LinqToSqlBinder : IQueryBinder { private readonly DataContext dataContext; public LinqToSqlBinder(DataContext dataContext) { this.dataContext = dataContext; } #region IQueryBinder Members public IQueryable GetQueryBoundToNewProvider(Type type) { return dataContext.GetTable(type); } #endregion }public interface IQueryBinder { IQueryable GetQueryBoundToNewProvider(Type type); }
public class LinqToObjectsBinder : IQueryBinder { private readonly IEnumerable objectSource; public LinqToObjectsBinder(IEnumerable objectSource) { this.objectSource = objectSource; } #region IQueryBinder Members public IQueryable GetQueryBoundToNewProvider(Type type) { return objectSource.AsQueryable(); } #endregion }
SqlCacheDependency with Query Notifications
If at all you have to get the SqlCacheDependency working with Query Notifications feature of SQL Server 2005, these links will provide a head start.
#1 Query Notifications in ADO.NET 2.0
#2 Using and Monitoring SQL 2005 Query Notification
Article #1 is just excellent in that it answers all the questions you get on how query notifications work.
Article #2 mentions how to debug the query notification infrastructure using the Profiler.
It a great feature but I really don’t know why its a pain to use. I am no API design expert but I could say something is wrong there…
‘Reorder failed, see details below’ error in AjaxControlToolkit’s ReorderList
Reorder failed, see details below.
Failed to reorder.
The actual issue was the update method we had set on the object datasource control.
AjaxControlToolkit perfers to use TypeDescriptor.GetProperties(row) to build the parameters dictionaries for the update method.
Ideally it should have been using the UpdateTemplate of the reorderlist and UpdateParameters on object datasource combination.
Nevertheless the exception handling in AjaxControlToolkit made sure I never got the actual error and I was stuck on this.
Buggy exception handling is worser then no exception handling.
Finally I built the AjaxControlToolkit in debug mode and figured out what was the actual problem.
How a Data Source Control Creates Parameters for Data-bound Fields article on MSDN was also of help.
Hope this will help someone who stuck with this issue.
Resurrection in .NET
I’ve already described a form of resurrection. When the garbage collector places a reference to the object on the freachable queue, the object is reachable from a root and has come back to life. Eventually, the object’s Finalize method is called, no roots point to the object, and the object is dead forever after. But what if an object’s Finalize method executed code that placed a pointer to the object in a global or static variable?"
Partial Types in .NET 2.0
- Very large classes (where it’s cumbersome to navigate with an editor through a single file). Though the first question to ask is why the hell do you have such a large class ;-).
- Allowing multiple developers to work on a single class, without the need for later merging files in source control.
- Easing the writing of automatic code generators, such as visual designers. Code generators have had a hard time trying to manage the generated code when it was placed in the human-written code. Passive code generators generate code only once and do not think about modifying / updating the generated code. Active code generators on the other hand are more realistic, assume the need to modify the generated code. If you have to add your own code on top of the generated code, then it’s pain. I often faced this issue when i had to modify the VS.NET(wsdl.exe actually) generated proxy code for web service clients. My changes will be lost if I do a update web reference in VS.NET. The windows forms designer generated code does some active generation using regions. Partial types is an elegant way of doing active code generation.
- A class does not have access to the private members of another class. However, a nested class can access any part of the nesting class. By writing the unit test classes, occasionally, as a nested class is beneficial. But,keeping my code to be tested and a test code for it in the same file has two problems. My file becomes larger and it is also harder to exclude the tests from a build. Partial classes can help here. It allows me to keep the class in one file and its nested test class(es) in separate file(s). Via ‘The Agile Developer’ Venky.
- All the partial definitions must be qualified with the partial keyword.
- The whole class can inherit from only one class. This can be speicified in either one of the partial definitions or all of them. If inheritance is specified in more than one partial definition then it has to be from the same class.
- Each parital definition can add(implement) one or more interfaces.
- If any of the partial definitions are declared abstract, then the entire type is considered abstract.
- If any of the partial definitions are declared sealed, then the entire type is considered sealed.
- All of the partial definitions must be available at compile time to form the final type.
- All the partial definitions must have the same accessibility, such as public, private, and so on.